Compare commits

...

100 Commits

Author SHA1 Message Date
Marc Vertes
a241119bf7 interp: improve handling of aliased types
Avoid to test directly for a type category, as it may give wrong
results for aliased types, where the interesting category remains
masked.  Instead, use some property helpers, such as isFuncSrc,
isPtrSrc and isInterfaceSrc to check if a type is of source function,
source pointer or source interface respectively (versus runtime
defined function, pointer or interface).

Fixes #1068.
2021-04-14 17:52:04 +02:00
Marc Vertes
3e3f8d5c2f interp: implement unsafe.Offsetof
Offsetof returns the offset of a field in a struct. It is computed
during parsing at CFG, due to the constraint of operating on a
struct selector expression.

With this function, the support of 'unsafe' package is now
complete in yaegi.

Fixes #1062.
2021-04-14 14:28:03 +02:00
Marc Vertes
9aeb78fc36 interp: fix method lookup for aliased types
Fixes #1058.
2021-04-13 18:10:04 +02:00
Marc Vertes
7863456d52 interp: refactor slice type management
Add missing `sliceT` type category for consistency. Remove
`sizedef` field in `itype` struct. Rename field `size` to `length`.
Clean the various hacks used to cope with the absence of `sliceT`.
2021-04-13 18:00:12 +02:00
Ethan Reesor
428b658160 Allow colon-equal to override variable type
If I execute the following:

```
I := interp.New(interp.Options{})
I.Eval(`x := 1`)
I.Eval(`x := "foobar"`)
```

I expect the second declaration to override the first. `var x string` will override the previous type, and redeclaring a type, function, const, etc will override it, but the `:=` operator will not.

Currently, the result is: `reflect.Set: value of type string is not assignable to type int`
2021-04-02 09:08:04 +02:00
Ludovic Fernandez
350cf80bbf chore: restore macos env in GitHub Actions. 2021-04-02 08:48:04 +02:00
Ethan Reesor
d92051d40f interp: treat var declaration within a block as a define statement
This PR:

- Treats a `varDecl` within a block as a `defineStmt`
  - More specifically, any `varDecl` with a grandparent that is *not* a `fileStmt`
- Adds an extra condition to the handler for implicit const assignment
- Adds a tests to cover the changes
- Closes #1071
2021-04-01 15:22:03 +02:00
Marc Vertes
aa2621f6c6 interp: fix a panic when embedding an error interface
This patch brings the following modifications:
- consider that an interface is assignable to another if the former
  implements the latter
- call TypeOf() method instead of rtype field when resolving methods, to
  handle first met types
- unwrap error interface inplace rather than embedding it in an
  interface definition, as lower case named embbeded interface may
  not be handled by reflect when lookup for a method.

Fixes #1063. Partially improves #1058.
2021-04-01 10:38:05 +02:00
Marc Vertes
2b1d6f0e7a interp: fix append on variadic recursive struct
Fixes #1065. Improves #1058.
2021-03-26 09:34:03 +01:00
Ethan Reesor
992676722d interp: allow redeclaration of imports
This PR adds an interpreter option, `AllowRedeclaration`. If this option is set, `(*Interpreter).Eval` will allow package imports to be redeclared. That is, no error will be raised and the package symbol will be overwritten.

I would like to use Yaegi to power a Go notebook (VSCode extension), somewhat like Jupyter. A notebook can have multiple Go 'cells' which can be evaluated (using Yaegi). As much as is possible, evaluating cells should be idempotent - that is, evaluating a cell multiple times should have the same effect as evaluating it once, ideally. Cells that are not idempotent can degrade the user experience.

Specifically, Go files tend to declare all imports in a single block. In a notebook, I'd put all imports in a single block, in their own cell. When I decide I need to import an additional package, I want to add that import to the existing cell and evaluate it. Without this MR, reevaluating that block usually causes an error.
2021-03-25 23:34:03 +01:00
エガオノキミヘ
451c754068 extract: process interface wrapper method with variadic parameter
Fixes #1055
2021-03-24 11:48:03 +01:00
エガオノキミヘ
84ad46751a fix #1056, extract: replace the code for get package name.
fix #1056
2021-03-23 10:30:04 +01:00
Marc Vertes
ec5392d566 interp: do not skip use of tmp frame in multi-assign
Fixes #1052.
2021-03-19 11:24:04 +01:00
Marc Vertes
7d8fdbc1fc interp: fix closure in a struct field
Functions in a struct fields are always wrapped (as potentially
used by the runtime), so generate a function wrapper also for
closure when assigned to a struct field.

When such a function is called from the interpreter, ensure that
interface arguments are also wrapped so method and receiver resolution
can be performed.

Fixes partially #1043.
2021-03-11 15:26:04 +01:00
Marc Vertes
fdfcb9c1df interp: do not check properties of incomplete types
Fixes #1042.
2021-03-09 11:58:04 +01:00
Marc Vertes
a988459dcd interp: fix a memory management issue causing wrong closure context
The first change forces a variable definition to reallocate a
new memory slot to avoid corrupting a previously defined one in
a loop block.

The second change ensures that the frame clone operations obtains
a copy of the original data slice, to preserve the original context
set in a loop.

Fixes #1035.
2021-03-09 09:30:04 +01:00
Sylvain Muller
51e0b46256 Use fmt.Fprintln instead of fmt.Println in runCfg
As mentioned in #1030, when an Eval panic, it is print with `fmt.Println()` and not to the configured `interp.Options.Stderr`. According to https://github.com/traefik/yaegi/blob/master/interp/interp.go#L210, it should be removed in future version so I'm not sure if this pull request is necessary. However, it could fix the issue in the meanwhile.

Fixes #1030.
2021-02-20 12:08:03 +01:00
Chris Waldon
eb06aeeb26 fix: use portable path manipulation for choosing package names
This *should* fix the generated names of packages on windows, which currently take the form `C:\kdfjslfj\jkfsjldkfjsf\sjdkfldjf` (this was reported to me by someone attempting to generate yaegi symbols for one of my projects). That being said, I don't have a windows machine to test on.
2021-02-17 10:12:08 +01:00
Ludovic Fernandez
8bb5daf60e feat: update stdlib mapping for go1.16
- drop stdlib for go1.14
- generate stdlib for go1.16
-  update CI configuration.
2021-02-17 10:02:03 +01:00
Marc Vertes
ac80d1b3ed interp: fix setting interface objects from operators
This is a follow-up of #1017, generalizing the use of reflect.Set
method to set, and possibly overwrite, the concrete value of
interface objects all accross the implementation of operators.
Previous optimized implementation for non-interface objects is
preserved.
2021-02-08 18:00:04 +01:00
mpl
2e17cfab4f interp: do not wrap empty interface
The empty interface (interface{}), and its variants (such as []interface{} and map[string]interface{}), are commonly used in Go to (json) Unmarshal arbitrary data. Within Yaegi, all interface types are wrapped in a valueInterface struct in order to retain all the information needed for a consistent internal state (as reflect is not enough to achieve that). However, this wrapping ends up being problematic when it comes to the type assertions related to the aforementioned Unmarshaling.

Therefore, this PR is an attempt to consider the empty interface (and its variants) as an exception within Yaegi, that should never be wrapped within a valueInterface, and to treat it similarly to the other basic Go types. The assumption is that the wrapping should not be needed, as there is no information about implemented methods to maintain.

Fixes #984 
Fixes #829 
Fixes #1015
2021-02-04 12:08:04 +01:00
Marc Vertes
3f4e1665b1 interp: fix default type for constants from runtime
The default type must be derived from the constant value when necessary,
otherwise the type check fails wrongly.

Fixes #1026.
2021-02-03 11:48:03 +01:00
Marc Vertes
b9b0897d95 interp: fix nil value check in case of interface
A wrong logic was leading to panic in recover. Simplify the
workflow for clarity.

Fixes #1022.
2021-02-02 13:28:04 +01:00
mpl
6337f8bc01 interp: clarify error about GOPATH probably not set 2021-02-02 10:10:03 +01:00
Ludovic Fernandez
ccb8072759 chore: use GitHub Actions.
- use GitHub Actions instead of TravisCI
- updates golangci-lint to v1.36.0 and applies my rules
2021-02-01 12:23:29 +01:00
Nicholas Wiersma
d73111cda1 fix: untyped check
When checking for untyped values, we can be sure at this stage that they must be a const value or already untyped. Checking for type string equality is no longer a good measure.

Fixes #1000
2021-01-28 16:20:04 +01:00
Nicholas Wiersma
ff521ecb1a fix: handle function references in composite bin map
When passing a function reference as an interface in a composite binary map, the case should be handled to not take the value of the the node.

Related to #886
2021-01-28 15:42:03 +01:00
Marc Vertes
61b4980077 interp: do not panic in case of invalid constant definition
This is a follow-up of #1014, where an invalid constant definition  involving a builtin is now checked at CFG. In addition, some missing arithmetic operators are now detected for assign optimization.
2021-01-27 12:58:03 +01:00
Marc Vertes
100d090853 interp: fix sending object implementing an interface through channel
A channel can be used to interchange data with the pre-compiled
runtime and therefore objects impletementing interfaces must be
wrapped if necessary, using genInterfaceWrapper.

A similar treatment could be applied when sending interpreted
functions over a channel, to be provided in a new PR.

Fixes #1010.
2021-01-26 18:58:04 +01:00
Marc Vertes
bd60de5995 interp: allow early constant evaluation from builtin call
One builtin has been identified to be used for constant definition:
len(), with a constant string argument. Add support for this.

Fixes #1012.
2021-01-26 11:12:04 +01:00
Marc Vertes
274eecdf18 interp: fix type recursivity detection
Fix the logic to detect recursive struct types, which was giving a false positive.
We now use the local type name  as key in tracker map.

A non-regression test case is included (_test/struct49.go).

This completes #1008.
2021-01-19 19:32:05 +01:00
Marc Vertes
8fa00f826c interp: fix map assignment from arithmetic operations
The logic to trigger assigment optimizations has been refactored for
clarity, and to exclude assignments to map entries.

Fixes #981.
2021-01-18 19:04:05 +01:00
Marc Vertes
a64fe5b210 interp: fix detection of type recursivity
If a struct contains several fields of the same temporary incomplete
type, it could be detected incorrectly as a recursive struct. Pass
a copy of defined types map to avoid this issue.

Fixes #1007.
2021-01-15 12:14:04 +01:00
Marc Vertes
5c59dc425f interp: fix operators working on integer constants
Always attempt to obtain an integer constant value for operators
expecting so. It allows to use '/' in integer constant defintions,
instead of default big.Rat.

Fixes #1005
2021-01-14 17:26:06 +01:00
Marc Vertes
8ad14d8ea4 interp: handle aliased string used as a slice
Fixes #1002.
2021-01-14 16:48:06 +01:00
Marc Vertes
8a1f9ef44e interp: parse circular interface definitions
An undefined type detection function has been added to better diagnose
incomplete type definitions. Implicit type names in interface or struct
declarations are now better handled. The incomplete status is not
fowarded to aliased type declarations to handle circular definitions.

Fixes #999 and #995. Improves #260 (goes farther, but still fails).
2021-01-14 15:46:04 +01:00
Marc Vertes
5cd1e11379 chore: rename github to tap, following goreleaser deprecation notice
The release of v0.9.9 failed, due to
https://goreleaser.com/deprecations/#brewsgithub.
2021-01-06 09:14:04 +01:00
Marc Vertes
24b5375636 interp: fix memory handling of global values
In some cases, the global character of a value was lost, leading to
undefined behaviour. Now a node level field of -1 means that the value
is global, and that it should be accessed from the root data frame.

Fixes #993.
2021-01-05 17:28:03 +01:00
Marc Vertes
a83f492309 interp: add support for binary composite slice
Fixes #987.
2020-12-15 18:20:04 +01:00
mpl
02c30482cc interp: enable type assertion from empty interface into slice
Fixes #985
2020-12-15 17:28:04 +01:00
Marc Vertes
9e1da978b0 interp: fix handling interface types in wrapped functions
The interpreter interface type was replaced by a reflect.Value in
objects passed or return to function wrappers, losing the ability
to retrieve methods.

The valueInterface is now preserved, and correctly accessed if
wrapped multiple times.

Fixes #977.
2020-12-15 16:14:05 +01:00
mpl
662838fd80 interp: fix and refactor typeAssertStatus in
typeAssertStatus deals with the 3rd form of type assertion ("_, ok"), for
when one does not care about the result of the assertion itself.
Some cases for it, which are already fixed for the two other forms of
type assertions, had not been fixed for this form yet.

Therefore, this change fixes such cases for this form, while integrating
typeAssertStatus to the same code path as for the two other forms.
2020-12-07 15:58:04 +01:00
Marc Vertes
92d65c22f0 interp: remove incorrect type check on array object
The type check was generating false negatives. A correct test to check the
adressable status of an array is more complex to implement, and will
be done later.

Fixes #973.
2020-12-02 17:26:03 +01:00
mpl
2db4579b6f interp: fix short-form type assertions
The long-form (with comma-ok) ones were already fixed but the short-form
ones were not because they were in a completely different code path.

This PR also refactors the code so that both short-form and long-form
are now merged in the same function.

N.B: even though most (all?) cases seem to now be supported, one of them
still yields a result that does not satisfy reflect's Implements method
yet. It does not prevent the resulting assertion to be usable though.

N.B2: the code path for the third-form (_, ok) hasn't been fixed and/or
refactored yet.

Fixes #919
2020-12-02 14:46:03 +01:00
mpl
101633c380 interp: support two more type assertion cases
Fixes #967
2020-12-01 15:50:04 +01:00
mpl
1e0f6ece6e interp: support more type assertion cases
Fixes #955
2020-11-30 18:00:04 +01:00
Marc Vertes
662d2a6afe interp: fix parsing of assign to dereferenced pointer
Fixes #969.
2020-11-30 17:46:05 +01:00
Marc Vertes
b25ee3f809 interp: fix method lookup on aliased types
In aliased type declarations, when the target type was imported from
an external package rather than declared locally, the aliased type was
overwritten by target, loosing ability to lookup methods on the aliased
type. Aliasing on imported types is now properly detected and handled.

Fixes #971.
2020-11-30 15:44:04 +01:00
Marc Vertes
81e1e5f206 interp: handle getting address of interpreter interface value
Fixes #963.
2020-11-30 11:48:04 +01:00
Marc Vertes
81d8339132 test: chdir to package directory prior to launch tests
Because this is what `go test` does, and some packages depend on that,
for example `github.com/jjcollinge/servicefabric`.
2020-11-25 16:10:04 +01:00
Marc Vertes
d494f9e420 interp: support calling goto from sub-scope
As opposed to other symbols, goto labels must be searched in included
scopes, not upper ones. Implement scope.lookdown to perform this,
to allow calls to goto to be embedded in included scopes where label
is defined.

Fixes #953.
2020-11-19 12:48:03 +01:00
Marc Vertes
6da1107c39 fix: do not confuse function call with type conversion
Use node action to better discriminate between function call and type
conversion which have the same pattern at AST level.

Fixes #960.
2020-11-18 14:56:05 +01:00
Marc Vertes
38a7331bf9 interp: fix type check on function signature
Perform function declaration type check from the upper level scope (the scope where the
function is declared), to avoid possible collisions of local variables with package names.

Fixes #957.
2020-11-13 18:02:04 +01:00
Marc Vertes
13783889cb interp: avoid useless interface wrapping
in `callBin`, call arguments are converted to the corresponding
parameter type. In a case of an interface, the original concrete type
should be preserved instead, and only wrapped to an interface type for
internal interpreter types, as runtime values should already implement the
interface.

This change removes the interface wrapping when parameter is a runtime
value (valueT or ptrT to valueT).

This removes some overhead when handling runtime values, and keep a
similar behavior between interpreted and pre-compiled code. For
example, `io.Copy` preserves its internal optimisations when passed a
`bytes.Buffer`.
2020-11-12 10:48:04 +01:00
mpl
ed626f3fb9 interp: support conversion of runtime func into interp func
Conversion of interp func into runtime func already worked, but added a
test anyway to watch out for regressions.

Fixes #941
2020-11-10 00:40:04 +01:00
Marc Vertes
d0a34d467b interp: fix getting unsigned constant value
The function vUint, used to get the unsigned integer value of a value,
variable (frame) or constant, was broken for constant.Value expression.

Fixes #948.
2020-11-09 17:40:04 +01:00
Nicholas Wiersma
83676577ac fix: use the import path for types
When running GTA, the type `path` was set to `rpath`. This equates to the package path (`github.com/traefik/yaegi`) in most cases. In the vendored case the `rpath` is the sub package path `something/vendor/github.com/traefik/yaegi` causing issues in typecheck and likely further down the line. By using the `importPath` it makes this consistent.

**Note:** I have no clue how to test this decently. I am open to options here.

Potentially Fixes #916
2020-11-05 13:42:03 +01:00
mpl
f0fc907269 extract: support building/running yaegi with Go devel
Fixes #928
2020-11-05 11:40:04 +01:00
Marc Vertes
61f4704925 interp: fix CFG in case of for loop with empty init and cond
Refactor `for` variants for clarity. Ensure all possible 8 combinations
are covered.

Fixes #942.
2020-11-05 11:00:04 +01:00
Marc Vertes
b1ccfbf47f interp: apply type conversion on untyped variable at run
Fixes #938.
2020-11-04 18:16:04 +01:00
Marc Vertes
0ed4b362dc interp: implement conversion for interpreter function types
Interpreter function types are represented internally by the AST node
of their definition. The conversion operation creates a new node with
the type field pointing to the target type.

Fixes #936.
2020-11-03 17:48:03 +01:00
Marc Vertes
98807387a4 interp: resolve type for untyped shift expressions
A non-constant shift expression can be untyped, requiring to apply a
type from inherited context. This change insures that such context is
propagated during CFG pre-order walk, to be used if necessary.
    
Fixes #927.
2020-11-02 18:08:04 +01:00
Marc Vertes
c817823ba1 interp: fix incorrect infinite loop on for statement
Add a for statement variant for the case of a "for" with an init, no
condition and no post-increment.

Fixes #933.
2020-11-02 17:52:03 +01:00
mpl
3cb8bca81a interp: on panic, look for node where offending exec originated from
The execution flow is such that a node can end up running several chained exec
funcs, some of which actually originate from other nodes. For example, in:

var m []int // L0
println("hello world") // L1
m[0] = 1 // L2

the offending code is on a node on line 2 (out of range error). However, since
the assignment to m is part of the execution flow of the variable m, we'll get
the panic when running all the chained exec funcs attached to the node for m on
line 0.

Which is why, when that happens, we need to look for the actual node (the one on
L2) where the offending instruction originates from, in order to
properly report the origin of the panic.

Fixes #546
2020-11-02 10:26:04 +01:00
Marc Vertes
a38d19288f interp: fix testing for nil interface values
Fixes #924.
2020-10-30 16:20:04 +01:00
Marc Vertes
7f8ffa6719 interp: handle explicit nil values in literal composite values
Fixes #922.
2020-10-27 11:24:04 +01:00
Marc Vertes
513f5e37aa interp: fix type conversion for constant expressions
The case of a constant arithmetic expression being of float kind because
of quotient was not handled correctly. Simplify constant extraction to
reflect.Value first, then conversion to target type, and let reflect Convert
method panic if conversion is not valid.

Fixes #920.
2020-10-23 10:44:04 +02:00
Marc Vertes
9520a92241 interp: fix type in assign of shift operations
Type checking on shift operands was failing for untyped variable values.

Fix propagation of type in assignment. Optimize assignment of arithmetic
operations on variables by skipping the assign and writing directly to
destination frame value in the operator function.

Skip some slow tests when given -short test option.

Fixes #917.
2020-10-23 10:32:03 +02:00
rsteube
d47821bfaa interp: add indexExpr check for reflect.Array
In typecheck.go, type check was failing for expressions like `[2]int{0, 1}[0:]`.
```
cannot slice type [2]int
```

Fixes #914 .
2020-10-21 23:00:04 +02:00
rsteube
d7ede8ed5c interp: added sliceExpr and starExpr
Type check was failing for expression such as: `&(*tree)[node:][0]`, as in

```go
tree, _ := huffmanTreePool.Get().(*[]huffmanTree)
...

initHuffmanTree(&(*tree)[node:][0], histogram[l], -1, int16(l))
```

see c3da72aa01/brotli_bit_stream.go (L469)
2020-10-21 18:14:03 +02:00
Marc Vertes
22c63b225c interp: fix array size definition in case of forward declared constant
In type.go, the symbol lookup on constant id was not performed. Handle
the ident kind explicitely to allow that.

Fixes #911.
2020-10-21 13:08:03 +02:00
mpl
c0eaab0891 interp: fix assignable check
The assignable check used to be too strict as it lacked the property that
if an untyped const can be represented as a T, then it is assignable to T.

And we can now use that fixed check to add a missing check: in a return
statement, we now make sure that any of the returned elements are
assignable to what the signature tells us they should be.
2020-10-21 10:06:03 +02:00
rsteube
c74d050c5a interp: added kind to log for typecheck (#910) 2020-10-21 09:46:05 +02:00
Marc Vertes
804664c631 interp: fix type check in constant unary operations
In unary constant operations, the test for unsigned was defeated by
testing for int first, which is true also for unsigned. Make sure that
testing for unsigned precedes testing for int.

Fixes #907.
2020-10-19 10:32:03 +02:00
Kamil Samigullin
f6d8261a8a chore: add homebrew-tap recipe
Publish homebrew-tap recipe to https://github.com/traefik/homebrew-tap
2020-10-17 05:54:04 +02:00
Marc Vertes
68c02ce054 feature: expose extract package, previously internal 2020-10-15 18:58:03 +02:00
Marc Vertes
4b3e9ee231 feature: use environment in addition to flags to control test options
This applies to -syscall, -unsafe and -unrestricted flags with the
corresponding env variables YAEGI_SYSCALL, YAEGI_UNSAFE and
YAEGI_UNRESTRICTED, already used in the same way for the run
sub-command.
2020-10-15 18:42:04 +02:00
mpl
8916618a81 interp: API change for Symbols method
Often enough when debugging, one does not know exactly what argument
should be given to Symbols, as it's not always clear in which
scope/namespace the symbol one is looking for is stored in.

Therefore, this change enables the Symbols method to now take the empty
string as an argument, which results in all the known symbols to be
returned (keyed by import path).

As a consequence, when an non-empty argument is given, the returned
result should be similar to what we had before, except it is now
returned as the sole entry of an encompassing map.

In addition, the "binary" symbols (i.e. the ones ingested through a
Use call), are now also taken into account.
2020-10-15 10:02:04 +02:00
mpl
57b49f40d6 interp: make Symbols method take into account type objects too 2020-10-14 17:44:09 +02:00
Marc Vertes
190dade469 fix: detect a wrong number of arguments at return
Also fix error ouptut in the run command to avoid displaying twice
the same error.

Fixes #819.
2020-10-14 17:30:04 +02:00
Marc Vertes
6b652ea485 feature: provide a version sub-command
A version sub-command is implemented.
A local install target is added to Makefile, to set version from git at build.

Fixes #840.
2020-10-14 16:24:05 +02:00
Marc Vertes
473bc63588 fix: behavior of select and time ticker
There was several issues:
- access to field on pointer to struct from runtime: fix in
  lookupBinField
- assign operation was skipped when performed in a comm clause
- the direction of comm clause was wrong if a channel send operation was
  performed in a body of a receive comm clause

Fixes #884.
2020-10-14 15:50:04 +02:00
Marc Vertes
e32da38ad0 fix: append from / to runtime defined array
Check first for runtime defined array (typ.cat of valueT)
to avoid checking inexisting properties (an panic), when
deciding to use appendSlice or not.

Fixes #880.
2020-10-13 17:14:04 +02:00
Marc Vertes
b2b519c2fd fix: correctly return constant expressions in functions
In the case of a function returning a single value, a constant
result could be ignored, and the function would return a zero value.

Fixes #889.
2020-10-13 17:02:04 +02:00
Marc Vertes
9491e58920 fix: correct handling of select comm clause with empty body
Fixes #887.
2020-10-13 11:24:04 +02:00
Marc Vertes
f362237ac5 fix: perform constant type conversion when required
Fixes #893.
2020-10-12 19:06:03 +02:00
Marc Vertes
a83ec1f925 fix: allow yaegi command to interpret itself
Since the introduction of restricted stdlib and syscall symbols, the
capability of yaegi to interpret itself was broken.
The use of unrestricted symbols is now also controlled by environment
variables, to allow propagation accross nested interpreters.
The interpreter Panic symbol was not wrapped, this is fixed now.
the import path resolution was failing if the working directory was
outside of GOPATH.
The documentation and readme have been ajusted.

Fixes #890.
2020-10-09 11:48:04 +02:00
mpl
155ca4e6ad interp: support implicit-type slice of pointers of struct composite literal
Also fix same case for implicit-type maps of etc.

Fixes #883
2020-10-09 10:32:03 +02:00
mpl
ca196a5768 interp: fix implicit type for bin composite lit case
When the type is implicit, the first element in the list of children is
not the type of the composite literal, but it is one of the actual
children, so it should not be discarded.

Fixes #862
2020-10-05 16:42:03 +02:00
Marc Vertes
b78d55c66b fix: compute array type size from constant expression
Fixes #875.
2020-10-05 16:12:04 +02:00
mpl
16f5586a11 interp: apply integer division when appropriate
When working with an untyped const expression involving a division, if
the default type of the result should be an int (for example because the
default types of all the operands are ints as well), then we should make
sure that the operation that is applied is indeed an integer division,
and that the type of the result is not a float.

This is achieved by using the QUO_ASSIGN operator, instead of the QUO
operator.

This should fix several problems lurking around, and it notably fixes
one of the visible consequences, which is a systematic panic when using
the REPL as a "calculator".

This incidentally also allows us to revert what was done in
5dfc3b86dc since it now turns out it was
just a hack to fix one of the symptoms.

Fixes #864
2020-10-05 10:50:03 +02:00
Julien Breux
c3cf301c60 fix: replace playground link in docs 2020-09-30 10:26:03 +02:00
Marc Vertes
0dde990d0b feature: improve extract, add unrestricted syscalls 2020-09-29 18:42:05 +02:00
Marc Vertes
4cfeb1946e fix: concurrent eval test 2020-09-29 15:22:04 +02:00
Nicholas Wiersma
ec64b006cf fix: convert struct tags properly 2020-09-29 09:22:04 +02:00
Nicholas Wiersma
f36d4e01eb fix: yaegi os.Args should contain the script name 2020-09-28 10:54:05 +02:00
mpl
5dfc3b86dc interp: fix division for const 2020-09-28 10:42:05 +02:00
mpl
3ae01a2af3 interp: refactor doComposite cases 2020-09-22 17:26:03 +02:00
Marc Vertes
c06f83f34a fix: correct access to parameter type for variadic binary methods 2020-09-22 15:18:03 +02:00
Ludovic Fernandez
a6d9c84a30 chore: adds a downloader script. 2020-09-20 18:26:03 +02:00
590 changed files with 14132 additions and 5551 deletions

67
.github/workflows/go-cross.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Build Cross OS
on:
push:
branches:
- master
pull_request:
jobs:
cross:
name: Go
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/yaegi
strategy:
matrix:
go-version: [ 1.15, 1.16 ]
os: [ubuntu-latest, macos-latest, windows-latest]
include:
- os: ubuntu-latest
go-path-suffix: /go
- os: macos-latest
go-path-suffix: /go
- os: windows-latest
go-path-suffix: \go
steps:
# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v2
with:
path: go/src/github.com/traefik/yaegi
# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod # Module download cache
~/.cache/go-build # Build cache (Linux)
~/Library/Caches/go-build # Build cache (Mac)
'%LocalAppData%\go-build' # Build cache (Windows)
key: ${{ runner.os }}-${{ matrix.go-version }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-${{ matrix.go-version }}-go-
- name: Setup GOPATH
run: go env -w GOPATH=${{ github.workspace }}${{ matrix.go-path-suffix }}
# TODO fail on windows
# - name: Tests
# run: go test -v -cover ./...
# env:
# GOPATH: ${{ github.workspace }}${{ matrix.go-path }}
- name: Build
run: go build -race -v -ldflags "-s -w" -trimpath

110
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,110 @@
name: Main
on:
push:
branches:
- master
pull_request:
env:
GO_VERSION: 1.16
GOLANGCI_LINT_VERSION: v1.36.0
jobs:
linting:
name: Linting
runs-on: ubuntu-latest
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check and get dependencies
run: |
go mod tidy
git diff --exit-code go.mod
# git diff --exit-code go.sum
go mod download
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
- name: Run golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
run: make check
generate:
name: Checks code and generated code
runs-on: ubuntu-latest
needs: linting
strategy:
matrix:
go-version: [ 1.15, 1.16 ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check generated code
run: |
rm -f interp/op.go
make generate
git update-index -q --refresh
CHANGED=$(git diff-index --name-only HEAD --)
test -z "$CHANGED" || echo $CHANGED
test -z "$CHANGED"
main:
name: Build and Test
runs-on: ubuntu-latest
needs: linting
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/yaegi
strategy:
matrix:
go-version: [ 1.15, 1.16 ]
steps:
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Check out code
uses: actions/checkout@v2
with:
path: go/src/github.com/traefik/yaegi
fetch-depth: 0
# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v2
with:
path: ./_test/tmp
key: ${{ runner.os }}-yaegi-${{ hashFiles('**//_test/tmp/') }}
restore-keys: |
${{ runner.os }}-yaegi-
- name: Setup GOPATH
run: go env -w GOPATH=${{ github.workspace }}/go
- name: Build
run: go build -v ./...
- name: Run tests
run: make tests
env:
GOPATH: ${{ github.workspace }}/go

42
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Release
on:
push:
tags:
- v[0-9]+.[0-9]+*
env:
GO_VERSION: 1.16
jobs:
release:
name: Create a release
runs-on: ubuntu-latest
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO }}

2
.gitignore vendored
View File

@@ -3,7 +3,7 @@
*.dot
.idea/
/yaegi
cmd/goexports/goexports
internal/cmd/extract/extract
example/inception/inception
_test/tmp/
/dist

View File

@@ -33,16 +33,23 @@
"gochecknoinits",
"gochecknoglobals",
"wsl",
"nlreturn",
"godox",
"funlen",
"gocognit",
"stylecheck",
"gomnd",
"testpackage",
"paralleltest",
"tparallel",
"goerr113",
"wrapcheck",
"nestif",
"exhaustive",
"nlreturn",
"exhaustivestruct",
"forbidigo",
"ifshort",
"errorlint", # TODO: must be reactivate before fixes
]
[issues]
@@ -61,3 +68,6 @@
[[issues.exclude-rules]]
path = "interp/interp.go"
text = "`out` can be `io.Writer`"
[[issues.exclude-rules]]
path = "interp/interp_eval_test.go"
linters = ["thelper"]

View File

@@ -26,31 +26,6 @@ builds:
- goos: darwin
goarch: 386
- id: goexports
binary: goexports
main: ./cmd/goexports/
goos:
- darwin
- linux
# - windows
- freebsd
- openbsd
- solaris
goarch:
- amd64
- 386
- arm
- arm64
goarm:
- 7
- 6
- 5
ignore:
- goos: darwin
goarch: 386
changelog:
sort: asc
filters:
@@ -70,3 +45,19 @@ archives:
format: zip
files:
- LICENSE
brews:
- tap:
owner: traefik
name: homebrew-tap
commit_author:
name: traefiker
email: 30906710+traefiker@users.noreply.github.com
folder: Formula
homepage: https://github.com/traefik/yaegi
description: |
Yaegi is Another Elegant Go Interpreter.
It powers executable Go scripts and plugins, in embedded interpreters
or interactive shells, on top of the Go runtime.
test: |
system "#{bin}/yaegi version"

View File

@@ -1,60 +0,0 @@
language: go
dist: xenial
branches:
only:
- master
- /^v\d+\.\d+(\.\d+)?(-\S*)?$/
notifications:
email:
on_success: never
on_failure: change
cache:
directories:
- $GOPATH/pkg/mod
matrix:
fast_finish: true
include:
- go: 1.14.x
- go: 1.15.x
env: STABLE=true
env:
global:
- GO111MODULE=on
go_import_path: github.com/traefik/yaegi
before_install:
# Install linters and misspell
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin ${GOLANGCI_LINT_VERSION}
- golangci-lint --version
install:
- echo "TRAVIS_GO_VERSION=$TRAVIS_GO_VERSION"
- go mod download
before_script:
- rm -f interp/op.go
- make generate
- git update-index -q --refresh
- CHANGED=$(git diff-index --name-only HEAD --)
- test -z "$CHANGED" || echo $CHANGED
- test -z "$CHANGED"
script:
- make check
- go build -v ./...
- make tests
deploy:
- provider: script
skip_cleanup: true
script: curl -sL https://git.io/goreleaser | bash
on:
tags: true
condition: $STABLE = true

View File

@@ -3,21 +3,28 @@ check:
golangci-lint run
# Generate stdlib/syscall/syscall_GOOS_GOARCH.go for all platforms
gen_all_syscall: cmd/goexports/goexports
@cd stdlib/syscall && \
for v in $$(go tool dist list); do \
gen_all_syscall: internal/cmd/extract/extract
@for v in $$(go tool dist list); do \
echo syscall_$${v%/*}_$${v#*/}.go; \
GOOS=$${v%/*} GOARCH=$${v#*/} go generate; \
GOOS=$${v%/*} GOARCH=$${v#*/} go generate ./stdlib/syscall ./stdlib/unrestricted; \
done
cmd/goexports/goexports: cmd/goexports/goexports.go
go generate cmd/goexports/goexports.go
internal/cmd/extract/extract:
rm -f internal/cmd/extract/extract
go generate ./internal/cmd/extract
generate: gen_all_syscall
go generate
tests:
GO111MODULE=off go test -v ./...
GO111MODULE=off go test -race ./interp
install:
GOFLAGS=-ldflags=-X=main.version=$$(git describe --tags) go install ./...
.PHONY: check gen_all_syscall gen_tests
tests:
go test -v ./...
go test -race ./interp
# https://github.com/goreleaser/godownloader
install.sh: .goreleaser.yml
godownloader --repo=traefik/yaegi -o install.sh .goreleaser.yml
.PHONY: check gen_all_syscall gen_tests generate_downloader internal/cmd/extract/extract install

View File

@@ -37,6 +37,12 @@ go get -u github.com/traefik/yaegi/cmd/yaegi
Note that you can use [rlwrap](https://github.com/hanslub42/rlwrap) (install with your favorite package manager),
and alias the `yaegi` command in `alias yaegi='rlwrap yaegi'` in your `~/.bashrc`, to have history and command line edition.
### CI Integration
```bash
curl -sfL https://raw.githubusercontent.com/traefik/yaegi/master/install.sh | bash -s -- -b $GOPATH/bin v0.9.0
```
## Usage
### As an embedded interpreter
@@ -68,7 +74,7 @@ func main() {
}
```
[Go Playground](https://play.golang.org/p/zzvw4VlerLP)
[Go Playground](https://play.golang.org/p/2n-EpZbMYI9)
### As a dynamic extension framework
@@ -106,7 +112,7 @@ func main() {
}
```
[Go Playground](https://play.golang.org/p/6SEAoaO7n0U)
[Go Playground](https://play.golang.org/p/WvwH4JqrU-p)
### As a command-line interpreter
@@ -122,10 +128,20 @@ Hello World
>
```
Or interpret Go files:
Note that in interactive mode, all stdlib package are pre-imported,
you can use them directly:
```console
$ yaegi cmd/yaegi/yaegi.go
$ yaegi
> reflect.TypeOf(time.Date)
: func(int, time.Month, int, int, int, int, int, *time.Location) time.Time
>
```
Or interpret Go packages, directories or files, including itself:
```console
$ yaegi -syscall -unsafe -unrestricted github.com/traefik/yaegi/cmd/yaegi
>
```

17
_test/a43.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
type T [l1 + l2]int
const (
l1 = 2
l2 = 3
)
func main() {
fmt.Println(T{})
}
// Output:
// [0 0 0 0 0]

12
_test/a44.go Normal file
View File

@@ -0,0 +1,12 @@
package main
var a = [max]int{}
const max = 32
func main() {
println(len(a))
}
// Output:
// 32

View File

@@ -1,10 +1,33 @@
package main
func main() {
b := 2
var a interface{} = 5 + b
b := 2 // int
var c int = 5 + b
println(c)
var d int32 = 6 + int32(b)
println(d)
var a interface{} = 7 + b
println(a.(int))
var e int32 = 2
var f interface{} = 8 + e
println(f.(int32))
a = 9 + e
println(a.(int32))
var g int = 2
a = 10 + g
println(a.(int))
}
// Output:
// 7
// 8
// 9
// 10
// 11
// 12

22
_test/addr1.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import "fmt"
type T struct {
A int
B int
}
func main() {
a := &[]T{
{1, 2},
{3, 4},
}
fmt.Println("a:", a)
x := &(*a)[1:][0]
fmt.Println("x:", x)
}
// Output:
// a: &[{1 2} {3 4}]
// x: &{3 4}

63
_test/addr2.go Normal file
View File

@@ -0,0 +1,63 @@
package main
import (
"encoding/xml"
"errors"
"fmt"
)
type Email struct {
Where string `xml:"where,attr"`
Addr string
}
func f(r interface{}) error {
return withPointerAsInterface(&r)
}
func withPointerAsInterface(r interface{}) error {
_ = (r).(*interface{})
rp, ok := (r).(*interface{})
if !ok {
return errors.New("cannot assert to *interface{}")
}
em, ok := (*rp).(*Email)
if !ok {
return errors.New("cannot assert to *Email")
}
em.Where = "work"
em.Addr = "bob@work.com"
return nil
}
func ff(s string, r interface{}) error {
return xml.Unmarshal([]byte(s), r)
}
func fff(s string, r interface{}) error {
return xml.Unmarshal([]byte(s), &r)
}
func main() {
data := `
<Email where='work'>
<Addr>bob@work.com</Addr>
</Email>
`
v := Email{}
err := f(&v)
fmt.Println(err, v)
vv := Email{}
err = ff(data, &vv)
fmt.Println(err, vv)
vvv := Email{}
err = ff(data, &vvv)
fmt.Println(err, vvv)
}
// Ouput:
// <nil> {work bob@work.com}
// <nil> {work bob@work.com}
// <nil> {work bob@work.com}

24
_test/addr3.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"fmt"
)
func main() {
var a interface{}
a = 2
fmt.Println(a)
var b *interface{}
b = &a
fmt.Println(*b)
var c **interface{}
c = &b
fmt.Println(**c)
}
// Output:
// 2
// 2
// 2

114
_test/addr4.go Normal file
View File

@@ -0,0 +1,114 @@
package main
import (
"encoding/json"
"fmt"
"log"
)
const jsonData = `[
"foo",
"bar"
]`
const jsonData2 = `[
{"foo": "foo"},
{"bar": "bar"}
]`
const jsonData3 = `{
"foo": "foo",
"bar": "bar"
}`
func fromSlice() {
var a []interface{}
var c, d interface{}
c = 2
d = 3
a = []interface{}{c, d}
if err := json.Unmarshal([]byte(jsonData), &a); err != nil {
log.Fatalln(err)
}
for k, v := range a {
fmt.Println(k, ":", v)
}
}
func fromEmpty() {
var a interface{}
var c, d interface{}
c = 2
d = 3
a = []interface{}{c, d}
if err := json.Unmarshal([]byte(jsonData), &a); err != nil {
log.Fatalln(err)
}
b := a.([]interface{})
for k, v := range b {
fmt.Println(k, ":", v)
}
}
func sliceOfObjects() {
var a interface{}
if err := json.Unmarshal([]byte(jsonData2), &a); err != nil {
log.Fatalln(err)
}
b := a.([]interface{})
for k, v := range b {
fmt.Println(k, ":", v)
}
}
func intoMap() {
var a interface{}
if err := json.Unmarshal([]byte(jsonData3), &a); err != nil {
log.Fatalln(err)
}
b := a.(map[string]interface{})
seenFoo := false
for k, v := range b {
vv := v.(string)
if vv != "foo" {
if seenFoo {
fmt.Println(k, ":", vv)
break
}
kk := k
vvv := vv
defer fmt.Println(kk, ":", vvv)
continue
}
seenFoo = true
fmt.Println(k, ":", vv)
}
}
func main() {
fromSlice()
fromEmpty()
sliceOfObjects()
intoMap()
}
// Ouput:
// 0 : foo
// 1 : bar
// 0 : foo
// 1 : bar
// 0 : map[foo:foo]
// 1 : map[bar:bar]
// foo : foo
// bar : bar

62
_test/addr5.go Normal file
View File

@@ -0,0 +1,62 @@
package main
import (
"encoding/json"
"fmt"
"net/url"
)
func main() {
body := []byte(`{
"BODY_1": "VALUE_1",
"BODY_2": "VALUE_2",
"BODY_3": null,
"BODY_4": {
"BODY_1": "VALUE_1",
"BODY_2": "VALUE_2",
"BODY_3": null
},
"BODY_5": [
"VALUE_1",
"VALUE_2",
"VALUE_3"
]
}`)
values := url.Values{}
var rawData map[string]interface{}
err := json.Unmarshal(body, &rawData)
if err != nil {
fmt.Println("can't parse body")
return
}
for key, val := range rawData {
switch val.(type) {
case string, bool, float64:
values.Add(key, fmt.Sprint(val))
case nil:
values.Add(key, "")
case map[string]interface{}, []interface{}:
jsonVal, err := json.Marshal(val)
if err != nil {
fmt.Println("can't encode json")
return
}
values.Add(key, string(jsonVal))
}
}
fmt.Println(values.Get("BODY_1"))
fmt.Println(values.Get("BODY_2"))
fmt.Println(values.Get("BODY_3"))
fmt.Println(values.Get("BODY_4"))
fmt.Println(values.Get("BODY_5"))
}
// Output:
// VALUE_1
// VALUE_2
//
// {"BODY_1":"VALUE_1","BODY_2":"VALUE_2","BODY_3":null}
// ["VALUE_1","VALUE_2","VALUE_3"]

15
_test/append1.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"bufio"
"bytes"
)
func main() {
s := bufio.NewScanner(bytes.NewReader([]byte("Hello\nTest\nLine3")))
s.Scan()
println(string(append(s.Bytes(), []byte(" World")...)))
}
// Output:
// Hello World

15
_test/append2.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"bufio"
"bytes"
)
func main() {
s := bufio.NewScanner(bytes.NewReader([]byte("Hello\nTest\nLine3")))
s.Scan()
println(string(append(s.Bytes(), " World"...)))
}
// Output:
// Hello World

128
_test/assert0.go Normal file
View File

@@ -0,0 +1,128 @@
package main
import (
"fmt"
"time"
)
type MyWriter interface {
Write(p []byte) (i int, err error)
}
type DummyWriter interface {
Write(p []byte) (i int, err error)
}
type TestStruct struct{}
func (t TestStruct) Write(p []byte) (n int, err error) {
return len(p), nil
}
func usesWriter(w MyWriter) {
n, _ := w.Write([]byte("hello world"))
fmt.Println(n)
}
type MyStringer interface {
String() string
}
type DummyStringer interface {
String() string
}
func usesStringer(s MyStringer) {
fmt.Println(s.String())
}
func main() {
// TODO(mpl): restore when we can deal with empty interface.
// var t interface{}
var t DummyWriter
t = TestStruct{}
var tw MyWriter
var ok bool
tw, ok = t.(MyWriter)
if !ok {
fmt.Println("TestStruct does not implement MyWriter")
} else {
fmt.Println("TestStruct implements MyWriter")
usesWriter(tw)
}
n, _ := t.(MyWriter).Write([]byte("hello world"))
fmt.Println(n)
// not redundant with the above, because it goes through a slightly different code path.
if _, ok := t.(MyWriter); !ok {
fmt.Println("TestStruct does not implement MyWriter")
return
} else {
fmt.Println("TestStruct implements MyWriter")
}
// TODO(mpl): restore
/*
t = 42
foo, ok := t.(MyWriter)
if !ok {
fmt.Println("42 does not implement MyWriter")
} else {
fmt.Println("42 implements MyWriter")
}
_ = foo
if _, ok := t.(MyWriter); !ok {
fmt.Println("42 does not implement MyWriter")
} else {
fmt.Println("42 implements MyWriter")
}
*/
// var tt interface{}
var tt DummyStringer
tt = time.Nanosecond
var myD MyStringer
myD, ok = tt.(MyStringer)
if !ok {
fmt.Println("time.Nanosecond does not implement MyStringer")
} else {
fmt.Println("time.Nanosecond implements MyStringer")
usesStringer(myD)
}
fmt.Println(tt.(MyStringer).String())
if _, ok := tt.(MyStringer); !ok {
fmt.Println("time.Nanosecond does not implement MyStringer")
} else {
fmt.Println("time.Nanosecond implements MyStringer")
}
// TODO(mpl): restore
/*
tt = 42
bar, ok := tt.(MyStringer)
if !ok {
fmt.Println("42 does not implement MyStringer")
} else {
fmt.Println("42 implements MyStringer")
}
_ = bar
if _, ok := tt.(MyStringer); !ok {
fmt.Println("42 does not implement MyStringer")
} else {
fmt.Println("42 implements MyStringer")
}
*/
}
// Output:
// TestStruct implements MyWriter
// 11
// 11
// TestStruct implements MyWriter
// time.Nanosecond implements MyStringer
// 1ns
// 1ns
// time.Nanosecond implements MyStringer

88
_test/assert1.go Normal file
View File

@@ -0,0 +1,88 @@
package main
import (
"fmt"
"reflect"
"time"
)
type TestStruct struct{}
func (t TestStruct) String() string {
return "hello world"
}
type DummyStringer interface{
String() string
}
func main() {
aType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
var t interface{}
t = time.Nanosecond
s, ok := t.(fmt.Stringer)
if !ok {
fmt.Println("time.Nanosecond does not implement fmt.Stringer")
return
}
fmt.Println(s.String())
fmt.Println(t.(fmt.Stringer).String())
bType := reflect.TypeOf(time.Nanosecond)
fmt.Println(bType.Implements(aType))
// not redundant with the above, because it goes through a slightly different code path.
if _, ok := t.(fmt.Stringer); !ok {
fmt.Println("time.Nanosecond does not implement fmt.Stringer")
return
} else {
fmt.Println("time.Nanosecond implements fmt.Stringer")
}
t = 42
foo, ok := t.(fmt.Stringer)
if !ok {
fmt.Println("42 does not implement fmt.Stringer")
} else {
fmt.Println("42 implements fmt.Stringer")
return
}
_ = foo
if _, ok := t.(fmt.Stringer); !ok {
fmt.Println("42 does not implement fmt.Stringer")
} else {
fmt.Println("42 implements fmt.Stringer")
return
}
// TODO(mpl): restore when fixed
// var tt interface{}
var tt DummyStringer
tt = TestStruct{}
ss, ok := tt.(fmt.Stringer)
if !ok {
fmt.Println("TestStuct does not implement fmt.Stringer")
return
}
fmt.Println(ss.String())
fmt.Println(tt.(fmt.Stringer).String())
if _, ok := tt.(fmt.Stringer); !ok {
fmt.Println("TestStuct does not implement fmt.Stringer")
return
} else {
fmt.Println("TestStuct implements fmt.Stringer")
}
}
// Output:
// 1ns
// 1ns
// true
// time.Nanosecond implements fmt.Stringer
// 42 does not implement fmt.Stringer
// 42 does not implement fmt.Stringer
// hello world
// hello world
// TestStuct implements fmt.Stringer

15
_test/assign16.go Normal file
View File

@@ -0,0 +1,15 @@
package main
type H struct {
bits uint
}
func main() {
h := &H{8}
var x uint = (1 << h.bits) >> 6
println(x)
}
// Output:
// 4

View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"image"
)
func main() {
v := map[string]*image.Point{
"foo": {X: 3, Y: 2},
"bar": {X: 4, Y: 5},
}
fmt.Println(v["foo"], v["bar"])
}
// Output:
// (3,2) (4,5)

View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"image"
)
func main() {
v := []*image.Point{
{X: 3, Y: 2},
{X: 4, Y: 5},
}
fmt.Println(v)
}
// Output:
// [(3,2) (4,5)]

16
_test/binstruct_slice0.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"image"
)
func main() {
v := []image.Point{
{X: 3, Y: 2},
}
fmt.Println(v)
}
// Output:
// [(3,2)]

View File

@@ -4,10 +4,16 @@ func f(a []int) interface{} {
return cap(a)
}
func g(a []int) int {
return cap(a)
}
func main() {
a := []int{1, 2}
println(g(a))
println(f(a).(int))
}
// Output:
// 2
// 2

18
_test/closure10.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
foos := []func(){}
for i := 0; i < 3; i++ {
a, b := i, i
foos = append(foos, func() { println(i, a, b) })
}
foos[0]()
foos[1]()
foos[2]()
}
// Output:
// 3 0 0
// 3 1 1
// 3 2 2

22
_test/closure11.go Normal file
View File

@@ -0,0 +1,22 @@
package main
type T struct {
F func()
}
func main() {
foos := []T{}
for i := 0; i < 3; i++ {
a := i
foos = append(foos, T{func() { println(i, a) }})
}
foos[0].F()
foos[1].F()
foos[2].F()
}
// Output:
// 3 0
// 3 1
// 3 2

25
_test/closure12.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import "fmt"
type T struct {
F func()
}
func main() {
foos := []T{}
for i := 0; i < 3; i++ {
a := i
n := fmt.Sprintf("i=%d", i)
foos = append(foos, T{func() { println(i, a, n) }})
}
foos[0].F()
foos[1].F()
foos[2].F()
}
// Output:
// 3 0 i=0
// 3 1 i=1
// 3 2 i=2

18
_test/closure9.go Normal file
View File

@@ -0,0 +1,18 @@
package main
func main() {
foos := []func(){}
for i := 0; i < 3; i++ {
a := i
foos = append(foos, func() { println(i, a) })
}
foos[0]()
foos[1]()
foos[2]()
}
// Output:
// 3 0
// 3 1
// 3 2

19
_test/composite12.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type A struct {
C D
}
type D struct {
E string
}
func main() {
a := A{}
a.C = D{"bb"}
println(a.C.E)
}
// Output:
// bb

19
_test/composite13.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type A struct {
C D
}
type D struct {
E string
}
func main() {
a := A{}
a.C = D{E: "bb"}
println(a.C.E)
}
// Output:
// bb

15
_test/composite14.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
type T struct {
b []byte
}
func main() {
t := T{nil}
fmt.Println(t)
}
// Output:
// {[]}

52
_test/composite15.go Normal file
View File

@@ -0,0 +1,52 @@
package main
import (
"fmt"
)
func interfaceAsInts() {
var a interface{}
b := 2
c := 3
a = []int{b, c}
d, ok := a.([]int)
if !ok {
println("nope")
return
}
for _, v := range d {
fmt.Println(v)
}
}
func interfaceAsInterfaces() {
var a, b, c interface{}
b = 2
c = 3
a = []interface{}{b, c}
d, ok := a.([]interface{})
if !ok {
println("nope")
return
}
fmt.Println(d)
for _, v := range d {
fmt.Println(v)
}
}
func main() {
interfaceAsInts()
interfaceAsInterfaces()
}
// Output:
// 2
// 3
// [2 3]
// 2
// 3

16
_test/composite16.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"fmt"
"net/url"
)
func main() {
body := url.Values{
"Action": {"none"},
}
fmt.Println(body)
}
// Output:
// map[Action:[none]]

30
_test/composite17.go Normal file
View File

@@ -0,0 +1,30 @@
package main
import (
"html/template"
)
var str = `{{ stringOr .Data "test" }}`
func main() {
_, err := template.New("test").
Funcs(template.FuncMap{
"stringOr": stringOr,
}).
Parse(str)
if err != nil {
println(err.Error())
return
}
println("success")
}
func stringOr(v, def string) string {
if v == "" {
return def
}
return v
}
// Output:
// success

16
_test/composite8bis.go Normal file
View File

@@ -0,0 +1,16 @@
package main
type T struct{ I int }
func main() {
t := []*T{}
s := []int{1, 2}
for _, e := range s {
x := &T{I: e}
t = append(t, x)
}
println(t[0].I, t[1].I)
}
// Output:
// 1 2

12
_test/const16.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import (
"fmt"
)
func main() {
fmt.Println(7 / 3)
}
// Output:
// 2

14
_test/const17.go Normal file
View File

@@ -0,0 +1,14 @@
package main
import "fmt"
var t [7/3]int
func main() {
t[0] = 3/2
t[1] = 5/2
fmt.Println(t)
}
// Output:
// [1 2]

11
_test/const18.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "time"
func main() {
a := int64(time.Second)
println(a)
}
// Output:
// 1000000000

17
_test/const19.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import (
"fmt"
"time"
)
func get10Hours() time.Duration {
return 10 * time.Hour
}
func main() {
fmt.Println(get10Hours().String())
}
// Output:
// 10h0m0s

12
_test/const20.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import "fmt"
const maxLen = int64(int(^uint(0) >> 1))
func main() {
fmt.Println(maxLen)
}
// Output:
// 9223372036854775807

12
_test/const21.go Normal file
View File

@@ -0,0 +1,12 @@
package main
const a = 64
var b uint = a * a / 2
func main() {
println(b)
}
// Output:
// 2048

15
_test/const22.go Normal file
View File

@@ -0,0 +1,15 @@
package main
const (
numDec uint8 = (1 << iota) / 2
numHex
numOct
numFloat
)
func main() {
println(13 & (numHex | numOct))
}
// Output:
// 1

12
_test/const23.go Normal file
View File

@@ -0,0 +1,12 @@
package main
const maxlen = len("hello")
var gfm = [maxlen]byte{}
func main() {
println(len(gfm))
}
// Output:
// 5

14
_test/const24.go Normal file
View File

@@ -0,0 +1,14 @@
package main
var aa = [...]int{1, 2, 3}
const maxlen = cap(aa)
var gfm = [maxlen]byte{}
func main() {
println(len(gfm))
}
// Output:
// 3

11
_test/const25.go Normal file
View File

@@ -0,0 +1,11 @@
package main
const (
FGBlack Attribute = iota + 30
)
type Attribute int
func main() {
println(FGBlack)
}

21
_test/convert0.go Normal file
View File

@@ -0,0 +1,21 @@
package main
type T struct {
v int
}
type comparator func(T, T) bool
func sort(items []T, comp comparator) {
println("in sort")
}
func compT(t0, t1 T) bool { return t0.v < t1.v }
func main() {
a := []T{}
sort(a, comparator(compT))
}
// Output:
// in sort

17
_test/convert1.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "strconv"
type atoidef func(s string) (int, error)
func main() {
stdatoi := atoidef(strconv.Atoi)
n, err := stdatoi("7")
if err != nil {
panic(err)
}
println(n)
}
// Output:
// 7

19
_test/convert2.go Normal file
View File

@@ -0,0 +1,19 @@
package main
import "bufio"
func fakeSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
return 7, nil, nil
}
func main() {
splitfunc := bufio.SplitFunc(fakeSplitFunc)
n, _, err := splitfunc(nil, true)
if err != nil {
panic(err)
}
println(n)
}
// Output:
// 7

16
_test/for15.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func f() int { println("in f"); return 1 }
func main() {
for i := f(); ; {
println("in loop")
if i > 0 {
break
}
}
}
// Output:
// in f
// in loop

16
_test/for16.go Normal file
View File

@@ -0,0 +1,16 @@
package main
func main() {
max := 1
for ; ; max-- {
if max == 0 {
break
}
println("in for")
}
println("bye")
}
// Output:
// in for
// bye

View File

@@ -2,10 +2,15 @@ package main
func f1(a int) interface{} { return a + 1 }
func f2(a int64) interface{} { return a + 1 }
func main() {
c := f1(3)
println(c.(int))
b := f2(3)
println(b.(int64))
}
// Output:
// 4
// 4

View File

@@ -2,7 +2,13 @@ package main
func f1(a int) int { return a + 1 }
func f2(a int) interface{} { return f1(a) }
func f2(a int) interface{} {
// TODO: re-enable the optimized case below, once we've figured out why it
// interferes with the empty interface model.
// return f1(a)
var foo interface{} = f1(a)
return foo
}
func main() {
c := f2(3)

10
_test/fun23.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func f(x int) { return x }
func main() {
print("hello")
}
// Error:
// 3:17: too many arguments to return

10
_test/fun24.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func f(x int) (int, int) { return x, "foo" }
func main() {
print("hello")
}
// Error:
// cannot use "foo" (type stringT) as type intT in return argument

10
_test/fun25.go Normal file
View File

@@ -0,0 +1,10 @@
package main
func f(x string) (a int, b int) { return x, 5 }
func main() {
print("hello")
}
// Error:
// cannot use x (type stringT) as type intT in return argument

17
_test/fun26.go Normal file
View File

@@ -0,0 +1,17 @@
package main
type F func() (int, error)
func f1() (int, error) { return 3, nil }
func f2(a string, f F) {
c, _ := f()
println(a, c)
}
func main() {
f2("hello", F(f1))
}
// Output:
// hello 3

12
_test/goto1.go Normal file
View File

@@ -0,0 +1,12 @@
package main
func main() {
if true {
goto here
}
here:
println("ok")
}
// Output:
// ok

32
_test/interface47.go Normal file
View File

@@ -0,0 +1,32 @@
package main
type Doer interface {
Do() error
}
type T struct {
Name string
}
func (t *T) Do() error { println("in do"); return nil }
func f() (Doer, error) { return &T{"truc"}, nil }
type Ev struct {
doer func() (Doer, error)
}
func (e *Ev) do() {
d, _ := e.doer()
d.Do()
}
func main() {
e := &Ev{f}
println(e != nil)
e.do()
}
// Output:
// true
// in do

17
_test/interface48.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
type I1 interface{ A }
type A = I2
type I2 interface{ F() I1 }
func main() {
var i I1
fmt.Println(i)
}
// Output:
// <nil>

45
_test/interface49.go Normal file
View File

@@ -0,0 +1,45 @@
package main
type Descriptor interface {
ParentFile() FileDescriptor
}
type FileDescriptor interface {
Enums() EnumDescriptors
Services() ServiceDescriptors
}
type EnumDescriptors interface {
Get(i int) EnumDescriptor
}
type EnumDescriptor interface {
Values() EnumValueDescriptors
}
type EnumValueDescriptors interface {
Get(i int) EnumValueDescriptor
}
type EnumValueDescriptor interface {
Descriptor
}
type ServiceDescriptors interface {
Get(i int) ServiceDescriptor
}
type ServiceDescriptor interface {
Descriptor
isServiceDescriptor
}
type isServiceDescriptor interface{ ProtoType(ServiceDescriptor) }
func main() {
var d Descriptor
println(d == nil)
}
// Output:
// true

13
_test/interface50.go Normal file
View File

@@ -0,0 +1,13 @@
package main
func main() {
a := true
var b interface{} = 5
println(b.(int))
b = a == true
println(b.(bool))
}
// Output:
// 5
// true

23
_test/interface51.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type Error interface {
error
Message() string
}
type T struct {
Msg string
}
func (t *T) Error() string { return t.Msg }
func (t *T) Message() string { return "message:" + t.Msg }
func newError() Error { return &T{"test"} }
func main() {
e := newError()
println(e.Error())
}
// Output:
// test

40
_test/issue-1007.go Normal file
View File

@@ -0,0 +1,40 @@
package main
type TypeA struct {
B TypeB
}
type TypeB struct {
C1 *TypeC
C2 *TypeC
}
type TypeC struct {
Val string
D *TypeD
D2 *TypeD
}
type TypeD struct {
Name string
}
func build() *TypeA {
return &TypeA{
B: TypeB{
C2: &TypeC{Val: "22"},
},
}
}
func Bar(s string) string {
a := build()
return s + "-" + a.B.C2.Val
}
func main() {
println(Bar("test"))
}
// Output:
// test-22

22
_test/issue-1010.go Normal file
View File

@@ -0,0 +1,22 @@
package main
import (
"encoding/json"
"fmt"
)
type MyJsonMarshaler struct{ n int }
func (m MyJsonMarshaler) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`{"num": %d}`, m.n)), nil
}
func main() {
ch := make(chan json.Marshaler, 1)
ch <- MyJsonMarshaler{2}
m, err := json.Marshal(<-ch)
fmt.Println(string(m), err)
}
// Output:
// {"num":2} <nil>

17
_test/issue-1022.go Normal file
View File

@@ -0,0 +1,17 @@
package main
import "fmt"
func main() {
defer func() {
r := recover()
if r != nil {
fmt.Println(r)
}
}()
panic("Ho Ho Ho!")
}
// Output:
// Ho Ho Ho!

23
_test/issue-1052.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import "fmt"
func main() {
a, b := 1, 1
for i := 0; i < 10; i++ {
fmt.Println(a)
a, b = b, a+b
}
}
// Output:
// 1
// 1
// 2
// 3
// 5
// 8
// 13
// 21
// 34
// 55

20
_test/issue-1065.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import "fmt"
type AST struct {
Num int
Children []AST
}
func newAST(num int, root AST, children ...AST) AST {
return AST{num, append([]AST{root}, children...)}
}
func main() {
ast := newAST(1, AST{}, AST{})
fmt.Println(ast)
}
// Output:
// {1 [{0 []} {0 []}]}

19
_test/issue-1068.go Normal file
View File

@@ -0,0 +1,19 @@
package main
type I interface {
Hello()
}
type T struct{}
func (t T) Hello() { println("hello") }
type I2 I
func main() {
var i I2 = T{}
i.Hello()
}
// Output:
// hello

23
_test/issue-880.go Normal file
View File

@@ -0,0 +1,23 @@
package main
import (
"bufio"
"bytes"
)
func main() {
var buf1 = make([]byte, 1024)
var buf2 []byte
buf1 = []byte("Hallo\nTest\nLine3")
s := bufio.NewScanner(bytes.NewReader(buf1))
for s.Scan() {
buf2 = append(buf2, append(s.Bytes(), []byte("\n")...)...)
}
print(string(buf2))
}
// Output:
// Hallo
// Test
// Line3

15
_test/issue-981.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "fmt"
func main() {
dp := make(map[int]int)
dp[0] = 1
for i := 1; i < 10; i++ {
dp[i] = dp[i-1] + dp[i-2]
}
fmt.Printf("%v\n", dp)
}
// Output:
// map[0:1 1:1 2:2 3:3 4:5 5:8 6:13 7:21 8:34 9:55]

15
_test/issue-993.go Normal file
View File

@@ -0,0 +1,15 @@
package main
var m map[string]int64
func initVar() {
m = make(map[string]int64)
}
func main() {
initVar()
println(len(m))
}
// Output:
// 0

7
_test/m2/m2_test.go Normal file
View File

@@ -0,0 +1,7 @@
package m2
import "testing"
func TestM2(t *testing.T) {
t.Errorf("got %s, want %s", "AAA", "BBB")
}

View File

@@ -12,10 +12,16 @@ type Hi interface {
Hello() string
}
type Hey interface {
Hello() string
}
func (r *Root) Hello() string { return "Hello " + r.Name }
func main() {
var one interface{} = &One{Root{Name: "test2"}}
// TODO(mpl): restore when type assertions work again.
// var one interface{} = &One{Root{Name: "test2"}}
var one Hey = &One{Root{Name: "test2"}}
println(one.(Hi).Hello())
}

26
_test/method36.go Normal file
View File

@@ -0,0 +1,26 @@
package main
type I interface{ Hello() }
type T struct{ Name string }
func (t *T) Hello() { println("Hello", t.Name) }
type FT func(i I)
type ST struct{ Handler FT }
func newF() FT {
return func(i I) {
i.Hello()
}
}
func main() {
st := &ST{}
st.Handler = newF()
st.Handler(&T{"test"})
}
// Output:
// Hello test

28
_test/nil3.go Normal file
View File

@@ -0,0 +1,28 @@
package main
type I interface {
Hello()
}
type T struct {
h I
}
func (t *T) Hello() { println("Hello") }
func main() {
t := &T{}
println(t.h != nil)
println(t.h == nil)
t.h = t
println(t.h != nil)
println(t.h == nil)
t.h.Hello()
}
// Output:
// false
// true
// true
// false
// Hello

View File

@@ -1,18 +1,20 @@
package main
import "time"
import "fmt"
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(1e9)
time.Sleep(1e7)
c1 <- "one"
}()
go func() {
time.Sleep(2e9)
time.Sleep(2e7)
c2 <- "two"
}()

44
_test/select14.go Normal file
View File

@@ -0,0 +1,44 @@
package main
import (
"fmt"
"time"
)
const (
period = 100 * time.Millisecond
precision = 7 * time.Millisecond
)
func main() {
counter := 0
p := time.Now()
ticker := time.NewTicker(period)
ch := make(chan int)
go func() {
for i := 0; i < 3; i++ {
select {
case t := <-ticker.C:
counter = counter + 1
ch <- counter
if d := t.Sub(p) - period; d < -precision || d > precision {
fmt.Println("wrong delay", d)
}
p = t
}
}
ch <- 0
}()
for c := range ch {
if c == 0 {
break
}
println(c)
}
}
// Output:
// 1
// 2
// 3

23
_test/select15.go Normal file
View File

@@ -0,0 +1,23 @@
package main
type T struct {
c1 chan string
c2 chan string
}
func main() {
t := &T{}
t.c2 = make(chan string)
go func(c chan string) { c <- "done" }(t.c2)
select {
case msg := <-t.c1:
println("received from c1:", msg)
case <-t.c2:
}
println("Bye")
}
// Output:
// Bye

11
_test/slice.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "fmt"
func main() {
a := [2][2]int{{0, 1}, {2, 3}}
fmt.Println(a[0][0:])
}
// Output:
// [0 1]

24
_test/struct58.go Normal file
View File

@@ -0,0 +1,24 @@
package main
import (
"fmt"
"reflect"
)
type A struct {
Test string `tag:"test"`
}
func main() {
a := A{}
t := reflect.TypeOf(a)
f, ok := t.FieldByName("Test")
if !ok {
return
}
fmt.Println(f.Tag.Get("tag"))
}
// Output:
// test

41
_test/struct59.go Normal file
View File

@@ -0,0 +1,41 @@
package main
import (
"fmt"
)
type A struct {
B map[string]*B
C map[string]*C
}
type C struct {
D *D
E *E
}
type D struct {
F *F
G []G
}
type E struct {
H []H
F *F
}
type B struct{}
type F struct{}
type G struct{}
type H struct{}
func main() {
conf := &A{
B: make(map[string]*B),
C: make(map[string]*C),
}
fmt.Println(conf)
}
// Output:
// &{map[] map[]}

View File

@@ -2,12 +2,26 @@ package main
func main() {
var a interface{}
a = []int{3}
switch a.(type) {
case []int:
println("a is []int")
case []string:
println("a is []string")
}
var b interface{}
b = []string{"hello"}
switch b.(type) {
case []int:
println("b is []int")
case []string:
println("b is []string")
}
println("bye")
}
// Output:
// a is []int
// b is []string
// bye

20
_test/time14.go Normal file
View File

@@ -0,0 +1,20 @@
package main
import (
"fmt"
"time"
)
var t time.Time
func f() time.Time {
time := t
return time
}
func main() {
fmt.Println(f())
}
// Output:
// 0001-01-01 00:00:00 +0000 UTC

15
_test/time15.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import "time"
type TimeValue time.Time
func (v *TimeValue) decode() { println("in decode") }
func main() {
var tv TimeValue
tv.decode()
}
// Output:
// in decode

11
_test/time16.go Normal file
View File

@@ -0,0 +1,11 @@
package main
import "time"
func main() {
localTime := time.ANSIC
println(localTime)
}
// Output:
// Mon Jan _2 15:04:05 2006

View File

@@ -12,11 +12,13 @@ type S struct {
}
func main() {
size := unsafe.Sizeof(S{})
align := unsafe.Alignof(S{})
x := S{}
size := unsafe.Sizeof(x)
align := unsafe.Alignof(x.Y)
offset := unsafe.Offsetof(x.Z)
fmt.Println(size, align)
fmt.Println(size, align, offset)
}
// Output:
// 24 8
// 24 8 16

15
_test/var15.go Normal file
View File

@@ -0,0 +1,15 @@
package main
var a int = 2
func inca() {
a = a + 1
}
func main() {
inca()
println(a)
}
// Output:
// 3

16
_test/variadic10.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"log"
"os"
)
func main() {
logger := log.New(os.Stdout, "test ", log.Lmsgprefix)
logger.Printf("args: %v %v", 1, "truc")
logger.Printf("args: %v %v %v", 1, "truc", 2)
}
// Output:
// test args: 1 truc
// test args: 1 truc 2

View File

@@ -1,137 +0,0 @@
//go:generate go build
/*
Goexports generates wrappers of package exported symbols.
Output files are written in the current directory, and prefixed with the go version.
Usage:
goexports package...
Example:
goexports github.com/traefik/yaegi/interp
The same goexport program is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.
*/
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"io"
"log"
"os"
"path"
"runtime"
"strings"
"github.com/traefik/yaegi/extract"
)
// genLicense generates the correct LICENSE header text from the provided
// path to a LICENSE file.
func genLicense(fname string) (string, error) {
if fname == "" {
return "", nil
}
f, err := os.Open(fname)
if err != nil {
return "", fmt.Errorf("could not open LICENSE file: %v", err)
}
defer func() { _ = f.Close() }()
license := new(strings.Builder)
sc := bufio.NewScanner(f)
for sc.Scan() {
txt := sc.Text()
if txt != "" {
txt = " " + txt
}
license.WriteString("//" + txt + "\n")
}
if sc.Err() != nil {
return "", fmt.Errorf("could not scan LICENSE file: %v", err)
}
return license.String(), nil
}
var (
licenseFlag = flag.String("license", "", "path to a LICENSE file")
// TODO: deal with a module that has several packages (so there's only one go.mod file at the root of the project).
importPathFlag = flag.String("import_path", "", "the namespace for the symbols extracted from the argument. Not needed if the argument is from the stdlib, or if the name can be found in a go.mod")
)
func main() {
flag.Parse()
if flag.NArg() == 0 {
flag.Usage()
log.Fatalf("missing package path")
}
license, err := genLicense(*licenseFlag)
if err != nil {
log.Fatal(err)
}
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
ext := extract.Extractor{
Dest: path.Base(wd),
License: license,
}
goos, goarch := os.Getenv("GOOS"), os.Getenv("GOARCH")
skip := map[string]bool{}
if goos == "solaris" {
skip["syscall.RawSyscall6"] = true
skip["syscall.Syscall6"] = true
}
ext.Skip = skip
for _, pkgIdent := range flag.Args() {
var buf bytes.Buffer
importPath, err := ext.Extract(pkgIdent, *importPathFlag, &buf)
if err != nil {
log.Println(err)
continue
}
var oFile string
if pkgIdent == "syscall" {
oFile = strings.ReplaceAll(importPath, "/", "_") + "_" + goos + "_" + goarch + ".go"
} else {
oFile = strings.ReplaceAll(importPath, "/", "_") + ".go"
}
prefix := runtime.Version()
if runtime.Version() != "devel" {
parts := strings.Split(runtime.Version(), ".")
prefix = parts[0] + "_" + extract.GetMinor(parts[1])
}
f, err := os.Create(prefix + "_" + oFile)
if err != nil {
log.Fatal(err)
}
if _, err := io.Copy(f, &buf); err != nil {
_ = f.Close()
log.Fatal(err)
}
if err := f.Close(); err != nil {
log.Fatal(err)
}
}
}

View File

@@ -7,7 +7,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"strings"
"github.com/traefik/yaegi/extract"
@@ -15,11 +15,15 @@ import (
func extractCmd(arg []string) error {
var licensePath string
var importPath string
var name string
var exclude string
var include string
eflag := flag.NewFlagSet("run", flag.ContinueOnError)
eflag.StringVar(&licensePath, "license", "", "path to a LICENSE file")
eflag.StringVar(&importPath, "import_path", "", "the namespace for the extracted symbols")
eflag.StringVar(&name, "name", "", "the namespace for the extracted symbols")
eflag.StringVar(&exclude, "exclude", "", "comma separated list of regexp matching symbols to exclude")
eflag.StringVar(&include, "include", "", "comma separated list of regexp matching symbols to include")
eflag.Usage = func() {
fmt.Println("Usage: yaegi extract [options] packages...")
fmt.Println("Options:")
@@ -45,20 +49,32 @@ func extractCmd(arg []string) error {
return err
}
if name == "" {
name = filepath.Base(wd)
}
ext := extract.Extractor{
Dest: path.Base(wd),
Dest: name,
License: license,
}
if exclude != "" {
ext.Exclude = strings.Split(exclude, ",")
}
if include != "" {
ext.Include = strings.Split(include, ",")
}
r := strings.NewReplacer("/", "-", ".", "_")
for _, pkgIdent := range args {
var buf bytes.Buffer
importPath, err := ext.Extract(pkgIdent, importPath, &buf)
importPath, err := ext.Extract(pkgIdent, name, &buf)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
}
oFile := strings.ReplaceAll(importPath, "/", "_") + ".go"
oFile := r.Replace(importPath) + ".go"
f, err := os.Create(oFile)
if err != nil {
return err
@@ -86,7 +102,7 @@ func genLicense(fname string) (string, error) {
f, err := os.Open(fname)
if err != nil {
return "", fmt.Errorf("could not open LICENSE file: %v", err)
return "", fmt.Errorf("could not open LICENSE file: %w", err)
}
defer func() { _ = f.Close() }()
@@ -100,7 +116,7 @@ func genLicense(fname string) (string, error) {
license.WriteString("//" + txt + "\n")
}
if sc.Err() != nil {
return "", fmt.Errorf("could not scan LICENSE file: %v", err)
return "", fmt.Errorf("could not scan LICENSE file: %w", err)
}
return license.String(), nil

View File

@@ -14,6 +14,7 @@ The commands are:
help print usage information
run execute a Go program from source
test execute test functions in a Go package
version print version
Use "yaegi help <command>" for more information about a command.
@@ -37,6 +38,9 @@ func help(arg []string) error {
return run([]string{"-h"})
case Test:
return test([]string{"-h"})
case Version:
fmt.Println("Usage: yaegi version")
return nil
default:
return fmt.Errorf("help: invalid yaegi command: %v", cmd)
}

View File

@@ -6,6 +6,7 @@ import (
"go/build"
"io/ioutil"
"os"
"strconv"
"strings"
"github.com/traefik/yaegi/interp"
@@ -17,19 +18,21 @@ import (
func run(arg []string) error {
var interactive bool
var useSyscall bool
var useUnrestricted bool
var useUnsafe bool
var tags string
var cmd string
var err error
// The following flags are initialized from environment.
useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL"))
useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED"))
useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE"))
rflag := flag.NewFlagSet("run", flag.ContinueOnError)
rflag.BoolVar(&interactive, "i", false, "start an interactive REPL")
rflag.BoolVar(&useSyscall, "syscall", false, "include syscall symbols")
rflag.BoolVar(&useUnrestricted, "unrestricted", false, "include unrestricted symbols")
rflag.BoolVar(&useSyscall, "syscall", useSyscall, "include syscall symbols")
rflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "include unrestricted symbols")
rflag.StringVar(&tags, "tags", "", "set a list of build tags")
rflag.BoolVar(&useUnsafe, "unsafe", false, "include usafe symbols")
rflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "include unsafe symbols")
rflag.StringVar(&cmd, "e", "", "set the command to be executed (instead of script or/and shell)")
rflag.Usage = func() {
fmt.Println("Usage: yaegi run [options] [path] [args]")
@@ -46,31 +49,40 @@ func run(arg []string) error {
i.Use(interp.Symbols)
if useSyscall {
i.Use(syscall.Symbols)
// Using a environment var allows a nested interpreter to import the syscall package.
if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
return err
}
}
if useUnsafe {
i.Use(unsafe.Symbols)
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
return err
}
}
if useUnrestricted {
// Use of unrestricted symbols should always follow use of stdlib symbols, to update them.
// Use of unrestricted symbols should always follow stdlib and syscall symbols, to update them.
i.Use(unrestricted.Symbols)
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
return err
}
}
if cmd != "" {
_, err = i.Eval(cmd)
showError(err)
}
if len(args) == 0 {
if interactive || cmd == "" {
_, err = i.REPL()
showError(err)
_, err = i.REPL()
}
return err
}
// Skip first os arg to set command line as expected by interpreted main
path := args[0]
os.Args = arg[1:]
os.Args = arg
flag.CommandLine = flag.NewFlagSet(path, flag.ExitOnError)
if isFile(path) {
@@ -78,7 +90,6 @@ func run(arg []string) error {
} else {
_, err = i.EvalPath(path)
}
showError(err)
if err != nil {
return err
@@ -86,7 +97,6 @@ func run(arg []string) error {
if interactive {
_, err = i.REPL()
showError(err)
}
return err
}

View File

@@ -1,11 +1,14 @@
package main
import (
"errors"
"flag"
"fmt"
"go/build"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"testing"
@@ -18,22 +21,24 @@ import (
func test(arg []string) (err error) {
var (
bench string
benchmem bool
benchtime string
count string
cpu string
failfast bool
run string
short bool
tags string
useUnrestricted bool
useUnsafe bool
useSyscall bool
timeout string
verbose bool
bench string
benchmem bool
benchtime string
count string
cpu string
failfast bool
run string
short bool
tags string
timeout string
verbose bool
)
// The following flags are initialized from environment.
useSyscall, _ := strconv.ParseBool(os.Getenv("YAEGI_SYSCALL"))
useUnrestricted, _ := strconv.ParseBool(os.Getenv("YAEGI_UNRESTRICTED"))
useUnsafe, _ := strconv.ParseBool(os.Getenv("YAEGI_UNSAFE"))
tflag := flag.NewFlagSet("test", flag.ContinueOnError)
tflag.StringVar(&bench, "bench", "", "Run only those benchmarks matching a regular expression.")
tflag.BoolVar(&benchmem, "benchmem", false, "Print memory allocation statistics for benchmarks.")
@@ -45,9 +50,9 @@ func test(arg []string) (err error) {
tflag.BoolVar(&short, "short", false, "Tell long-running tests to shorten their run time.")
tflag.StringVar(&tags, "tags", "", "Set a list of build tags.")
tflag.StringVar(&timeout, "timeout", "", "If a test binary runs longer than duration d, panic.")
tflag.BoolVar(&useUnrestricted, "unrestricted", false, "Include unrestricted symbols.")
tflag.BoolVar(&useUnsafe, "unsafe", false, "Include usafe symbols.")
tflag.BoolVar(&useSyscall, "syscall", false, "Include syscall symbols.")
tflag.BoolVar(&useUnrestricted, "unrestricted", useUnrestricted, "Include unrestricted symbols.")
tflag.BoolVar(&useUnsafe, "unsafe", useUnsafe, "Include usafe symbols.")
tflag.BoolVar(&useSyscall, "syscall", useSyscall, "Include syscall symbols.")
tflag.BoolVar(&verbose, "v", false, "Verbose output: log all tests as they are run.")
tflag.Usage = func() {
fmt.Println("Usage: yaegi test [options] [path]")
@@ -98,18 +103,40 @@ func test(arg []string) (err error) {
testing.Init()
os.Args = tf
flag.Parse()
path += string(filepath.Separator)
var dir string
switch strings.Split(path, string(filepath.Separator))[0] {
case ".", "..", string(filepath.Separator):
dir = path
default:
dir = filepath.Join(build.Default.GOPATH, "src", path)
}
if err = os.Chdir(dir); err != nil {
return err
}
i := interp.New(interp.Options{GoPath: build.Default.GOPATH, BuildTags: strings.Split(tags, ",")})
i.Use(stdlib.Symbols)
i.Use(interp.Symbols)
if useSyscall {
i.Use(syscall.Symbols)
// Using a environment var allows a nested interpreter to import the syscall package.
if err := os.Setenv("YAEGI_SYSCALL", "1"); err != nil {
return err
}
}
if useUnrestricted {
i.Use(unrestricted.Symbols)
if err := os.Setenv("YAEGI_UNRESTRICTED", "1"); err != nil {
return err
}
}
if useUnsafe {
i.Use(unsafe.Symbols)
if err := os.Setenv("YAEGI_UNSAFE", "1"); err != nil {
return err
}
}
if err = i.EvalTest(path); err != nil {
return err
@@ -117,7 +144,11 @@ func test(arg []string) (err error) {
benchmarks := []testing.InternalBenchmark{}
tests := []testing.InternalTest{}
for name, sym := range i.Symbols(path) {
syms, ok := i.Symbols(path)[path]
if !ok {
return errors.New("No tests found")
}
for name, sym := range syms {
switch fun := sym.Interface().(type) {
case func(*testing.B):
benchmarks = append(benchmarks, testing.InternalBenchmark{name, fun})

View File

@@ -71,7 +71,16 @@ Options:
-unsafe
include unsafe symbols.
Debugging support (may be removed at any time):
Environment variables:
YAEGI_SYSCALL=1
Include syscall symbols (same as -syscall flag).
YAEGI_UNRESTRICTED=1
Include unrestricted symbols (same as -unrestricted flag).
YAEGI_UNSAFE=1
Include unsafe symbols (same as -unsafe flag).
YAEGI_PROMPT=1
Force enable the printing of the REPL prompt and the result of last instruction,
even if stdin is not a terminal.
YAEGI_AST_DOT=1
Generate and display graphviz dot of AST with dotty(1)
YAEGI_CFG_DOT=1
@@ -90,6 +99,8 @@ import (
"fmt"
"log"
"os"
"github.com/traefik/yaegi/interp"
)
const (
@@ -97,8 +108,11 @@ const (
Help = "help"
Run = "run"
Test = "test"
Version = "version"
)
var version = "devel" // This may be overwritten at build time.
func main() {
var cmd string
var err error
@@ -119,6 +133,8 @@ func main() {
err = run(os.Args[2:])
case Test:
err = test(os.Args[2:])
case Version:
fmt.Println(version)
default:
// If no command is given, fallback to default "run" command.
// This allows scripts starting with "#!/usr/bin/env yaegi",
@@ -129,8 +145,10 @@ func main() {
}
if err != nil && !errors.Is(err, flag.ErrHelp) {
err = fmt.Errorf("%s: %w", cmd, err)
fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, fmt.Errorf("%s: %w", cmd, err))
if p, ok := err.(interp.Panic); ok {
fmt.Fprintln(os.Stderr, string(p.Stack))
}
exitCode = 1
}
os.Exit(exitCode)

View File

@@ -50,9 +50,9 @@ func TestYaegiCmdCancel(t *testing.T) {
yaegi := filepath.Join(tmp, "yaegi")
build := exec.Command("go", "build", "-race", "-o", yaegi, ".")
err = build.Run()
out, err := build.CombinedOutput()
if err != nil {
t.Fatalf("failed to build yaegi command: %v", err)
t.Fatalf("failed to build yaegi command: %v: %s", err, out)
}
// Test src must be terminated by a single newline.
@@ -82,7 +82,7 @@ func TestYaegiCmdCancel(t *testing.T) {
if err != nil {
t.Errorf("failed pipe test source to yaegi command: %v", err)
}
Sleep(200 * time.Millisecond)
Sleep(500 * time.Millisecond)
err = cmd.Process.Signal(os.Interrupt)
if err != nil {
t.Errorf("failed to send os.Interrupt to yaegi command: %v", err)

View File

@@ -0,0 +1,13 @@
package main
import (
"fmt"
"guthib.com/bat/baz"
)
func main() {
t := baz.NewT()
fmt.Printf("%s", t.A3)
}

View File

@@ -0,0 +1,22 @@
package baz
func NewT() *T {
return &T{
A1: make([]U, 0),
A3: "foobar",
}
}
type T struct {
A1 []U
A3 string
}
type U struct {
B1 V
B2 V
}
type V struct {
C1 string
}

View File

@@ -93,12 +93,18 @@ func TestPackages(t *testing.T) {
expected: "Yo hello",
evalFile: "./_pkg12/src/guthib.com/foo/main.go",
},
{
desc: "eval main with vendor",
goPath: "./_pkg13/",
expected: "foobar",
evalFile: "./_pkg13/src/guthib.com/foo/bar/main.go",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
goPath, err := filepath.Abs(test.goPath)
goPath, err := filepath.Abs(filepath.FromSlash(test.goPath))
if err != nil {
t.Fatal(err)
}
@@ -109,7 +115,7 @@ func TestPackages(t *testing.T) {
var msg string
if test.evalFile != "" {
if _, err := i.EvalPath(test.evalFile); err != nil {
if _, err := i.EvalPath(filepath.FromSlash(test.evalFile)); err != nil {
fatalStderrf(t, "%v", err)
}
msg = stdout.String()
@@ -140,6 +146,8 @@ func TestPackages(t *testing.T) {
}
func fatalStderrf(t *testing.T, format string, args ...interface{}) {
t.Helper()
fmt.Fprintf(os.Stderr, format+"\n", args...)
t.FailNow()
}
@@ -153,7 +161,7 @@ func TestPackagesError(t *testing.T) {
{
desc: "different packages in the same directory",
goPath: "./_pkg9/",
expected: "1:21: import \"github.com/foo/pkg\" error: found packages pkg and pkgfalse in _pkg9/src/github.com/foo/pkg",
expected: `1:21: import "github.com/foo/pkg" error: found packages pkg and pkgfalse in ` + filepath.FromSlash("_pkg9/src/github.com/foo/pkg"),
},
}

Some files were not shown because too many files have changed in this diff Show More