| Commit message (Collapse) | Author | Age | Files | Lines |
|
|
|
|
| |
closes #4774, closes #7385, closes #10019, closes #12405, closes #12732,
closes #13270, closes #13799, closes #15247, closes #16128, closes
#16175, closes #16774, closes #17527, closes #20880, closes #21346
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
follows up #24095
In #24095 a check was added that used `iterOverType` to check if a type
contained unresolved types, with the aim of always treating
`tyGenericBody` as resolved. But the body of the `tyGenericBody` is also
iterated over in `iterOverType`, so if the body of the type actually
used generic parameters (which isn't the case in the test added in
#24095, but is now), the check would still count the type as unresolved.
This is handled by not iterating over the children of `tyGenericBody`,
the only users of `iterOverType` are `containsGenericType` and
`containsUnresolvedType`, the first one always returns true for
`tyGenericBody` and the second one aims to always return false.
Unfortunately this means `iterOverType` isn't as generic of an API
anymore but maybe it shouldn't be used anymore for these procs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes regression remaining after #24092
In #24092 `prepareNode` was updated so it wouldn't try to instantiate
generic type symbols (like `Generic` when `type Generic[T] = object`,
and `prepareNode` is what `tyFromExpr` uses in most of the compiler. An
exception is in sigmatch, which is now changed to use `prepareNode` to
make generic type symbols work in the same way as usual. However this
requires another change to work:
Dot fields and matches to `typedesc` on generic types generate
`tyFromExpr` in generic contexts since #24005, including generic type
symbols. But this means when we try to instantiate the `tyFromExpr` in
sigmatch, which increases `c.inGenericContext` for potentially remaining
unresolved expressions, dotcalls stay as `tyFromExpr` and so never
match. To fix this, we change the "generic type" check in dot fields and
`typedesc` matching to an "unresolved type" check which excludes generic
body types; and for generic body types, we only generate `tyFromExpr` if
the dot field is a generic parameter of the generic type (so that it
gets resolved only at instantiation).
Notes for the future:
* Sigmatch shouldn't have to `inc c.inGenericContext`, if a `tyFromExpr`
can't instantiate it's fine if we just fail the match (i.e. redirect the
instantiation errors from `semtypinst` to a match failure). Then again
maybe this is the best way to check for inability to instantiate.
* The `elif c.inGenericContext > 0 and t.containsUnresolvedType` check
in dotfields could maybe be simplified to just checking for `tyFromExpr`
and `tyGenericParam`, but I don't know if this is an exhaustive list.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #24090
Generic body types are normally a sign of an uninstantiated type, and so
give errors when trying to instantiate them. However when instantiating
free user expressions like the nodes of `tyFromExpr`, generic default
params, static values etc, they can be used as arguments to macros or
templates etc (as in the issue). So, we don't try to instantiate generic
body type symbols at all in free expressions such as these (but not in
for example type nodes), and avoid the error.
In the future there should be a "concrete type" check for generic body
types different from the check in type instantiation to deal with things
like #24091, if we do want to allow this use of them.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Caught in https://github.com/metagn/applicates, I'm not sure which
commit causes this but it's also in the 2.0 branch (but not 2.0.2), so
it's not any recent PRs.
If a proc has a static parameter with type `static Foo[T]`, then another
parameter with type `static Bar[T, U]`, the generic instantiation for
`Bar` doesn't match `U` which has type `tyGenericParam`, but matches `T`
since it has type `tyTypeDesc`. The reason is that `concreteType`
returns the type itself for `tyTypeDesc` if `c.isNoCall` (i.e. matching
a generic invocation), but returns `nil` for `tyGenericParam`. I'm
guessing `tyGenericParam` is received here because of #22618, but that
doesn't explain why `T` is still `tyTypeDesc`. I'm not sure.
Regardless, we can just copy the behavior for `tyTypeDesc` to
`tyGenericParam` and also return the type itself when `c.isNoCall`. This
feels like it defeats the purpose of `concreteType` but the way it's
used doesn't make sense without it (generic param can't match another
generic param?). Alternatively we could loosen the `if concrete == nil:
return isNone` checks in some places for specific conditions, whether
`c.isNoCall` or `c.inGenericContext == 0` (though this would need
#24005).
|
|
|
|
|
|
|
|
|
| |
fixes #15959
Another followup of #22029 and #24005, subscript expressions now
recognize when their parameters are generic types, then generating
tyFromExpr. `typeof` also now properly sets `tfNonConstExpr` to make it
usable in proc signatures. `lent` with brackets like `lent[T]` is also
now allowed.
|
|
|
|
|
|
|
|
| |
fixes #22342, fixes #22607
Another followup of #22029, `when` expressions in general in generic
type bodies now behave like `nkRecWhen` does since #24042, leaving them
as `tyFromExpr` if a condition is uncertain. The tests for the issues
were originally added but left disabled in #24005.
|
|
|
|
|
|
|
| |
fixes #13979
Fixes templates in generics that use identifiers that aren't defined
yet, giving an early `undeclared identifier` error, by just marking
template bodies as in a mixin context in `semgnrc`.
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #24044
When matching a `tyFromExpr` against a `static` generic parameter,
`paramTypesMatch` tries to evaluate it as a constant expression, which
causes a segfault in the case of #24044. In #24005 a consequence of the
same behavior was an issue where `nkStaticExpr` was created for
`tyFromExpr` which made it not instantiate, so only the generation of
`nkStaticExpr` was disabled. Instead we now just completely ignore
`tyFromExpr` matching a `static` generic parameter in generic contexts
and keep it untouched.
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #24048
Generic lambdas get instantiated via `replaceTypesInBody` which calls
`replaceTypeVarsN` on the body of the lambda. This body can contain sym
nodes of gensym symbols generated by macros, which have `nil` type. But
a piece of code in `replaceTypeVarsN` checks whether the type of a
symbol is equal to `void` without checking if it's `nil` first, which
causes a segfault. Now it also checks that the type of the symbol isn't
`nil` for it to be `void`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #16376
The way the compiler handled generic proc instantiations in calls (like
`foo[int](...)`) up to this point was to instantiate `foo[int]`, create
a symbol for the instantiated proc (or a symchoice for multiple procs
excluding ones with mismatching generic param counts), then perform
overload resolution on this symbol/symchoice. The exception to this was
when the called symbol was already a symchoice node, in which case it
wasn't instantiated and overloading was called directly ([these
lines](https://github.com/nim-lang/Nim/blob/b7b1313d21deb687adab2b4a162e716ba561a26b/compiler/semexprs.nim#L3366-L3371)).
This has several problems:
* Templates and macros can't create instantiated symbols, so they
couldn't participate in overloaded explicit generic instantiations,
causing the issue #16376.
* Every single proc that can be instantiated with the given generic
params is fully instantiated including the body. #9997 is about this but
isn't fixed here since the instantiation isn't in a call.
The way overload resolution handles explicit instantiations by itself is
also buggy:
* It doesn't check constraints.
* It allows only partially providing the generic parameters, which makes
sense for implicit generics, but can cause ambiguity in overloading.
Here is how this PR deals with these problems:
* Overload resolution now always handles explicit generic instantiations
in calls, in `initCandidate`, as long as the symbol resolves to a
routine symbol.
* Overload resolution now checks the generic params for constraints and
correct parameter count (ignoring implicit params). If these don't
match, the entire overload is considered as not matching and not
instantiated.
* Special error messages are added for mismatching/missing/extra generic
params. This is almost all of the diff in `semcall`.
* Procs with matching generic parameters now instantiate only the type
of the signature in overload resolution, not the proc itself, which also
works for templates and macros.
Unfortunately we can't entirely remove instantiations because overload
resolution can't handle some cases with uninstantiated types even though
it's resolved in the binding (see the last 2 blocks in
`texplicitgenerics`). There are also some instantiation issues with
default params that #24005 didn't fix but I didn't want this to become
the 3rd huge generics PR in a row so I didn't dive too deep into trying
to fix them. There is still a minor instantiation fix in `semtypinst`
though for subscripts in calls.
Additional changes:
* Overloading of `[]` wasn't documented properly, it somewhat is now
because we need to mention the limitation that it can't be done for
generic procs/types.
* Tests can now enable the new type mismatch errors with just
`-d:testsConciseTypeMismatch` in the command.
Package PRs:
- using fork for now:
[combparser](https://github.com/PMunch/combparser/pull/7) (partial
generic instantiation)
- merged: [cligen](https://github.com/c-blake/cligen/pull/233) (partial
generic instantiation but non-overloaded + template)
- merged: [neo](https://github.com/andreaferretti/neo/pull/56) (trying
to instantiate template with no generic param)
|
|
|
|
|
|
|
|
|
| |
fixes #24041
`when` statements in generic object types normally just leave their
conditions as expressions and still typecheck their branch bodies.
Instead of this, when the condition can be evaluated as a constant as
well as the ones before it and it resolves to `true`, it now uses the
body of that branch without typechecking the remaining ones.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
closes #1969, closes #7547, closes #7737, closes #11838, closes #12283,
closes #12714, closes #12720, closes #14053, closes #16118, closes
#19670, closes #22645
I was going to wait on these but regression tests even for recent PRs
are turning out to be important in wide reaching PRs like #24010.
The other issues with the working label felt either finnicky (#7385,
#9156, #12732, #15247), excessive to test (#12405, #12424, #17527), or I
just don't know what fixed them/what the issue was (#16128: the PR link
gives a server error by Github, #12457, #12487).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #15314, fixes #24002
The OpenSym behavior first added to generics in #23091 now also applies
to templates, since templates can also capture symbols that are meant to
be replaced by local symbols if the context imports symbols with the
same name, as in the issue #24002. The experimental switch
`templateOpenSym` is added to enable this behavior for templates only,
and the experimental switch `openSym` is added to enable it for both
templates and generics, and the documentation now mainly mentions this
switch.
Additionally the logic for `nkOpenSymChoice` nodes that were previously
wrapped in `nkOpenSym` now apply to all `nkOpenSymChoice` nodes, and so
these nodes aren't wrapped in `nkOpenSym` anymore. This means
`nkOpenSym` can only have children of kind `nkSym` again, so it is more
in line with the structure of symchoice nodes. As for why they aren't
merged with `nkOpenSymChoice` nodes yet, we need some way to signal that
the node shouldn't become ambiguous if other options exist at
instantiation time, we already captured a symbol at the beginning and
another symbol can only replace it if it's closer in scope and
unambiguous.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
updated version of #22193
After #22029 and the followups #23983 and #24005 which fixed issues with
it, `tyFromExpr` no longer match any proc params in generic type bodies
but delay all non-matching calls until the type is instantiated.
Previously the mechanism `fauxMatch` was used to pretend that any
failing match against `tyFromExpr` actually matched, but prevented the
instantiation of the type until later.
Since this mechanism is not needed anymore for `tyFromExpr`, it is now
only used for `tyError` to prevent cascading errors and changed to a
bool field for simplicity. A change in `semtypes` was also needed to
prevent calling `fitNode` on default param values resolving to type
`tyFromExpr` in generic procs for params with non-generic types, as this
would try to coerce the expression into a concrete type when it can't be
instantiated yet.
The aliases `tyProxy` and `tyUnknown` for `tyError` and `tyFromExpr` are
also removed for uniformity.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
(#24005)
fixes #4228, fixes #4990, fixes #7006, fixes #7008, fixes #8406, fixes
#8551, fixes #11112, fixes #20027, fixes #22647, refs #23854 and #23855
(remaining issue fixed), refs #8545 (works properly now with
`cast[static[bool]]` changed to `cast[bool]`), refs #22342 and #22607
(disabled tests added), succeeds #23194
Parameter and return type nodes in generic procs now undergo the same
`inGenericContext` treatment that nodes in generic type bodies do. This
allows many of the fixes in #22029 and followups to also apply to
generic proc signatures. Like #23983 however this needs some more
compiler fixes, but this time mostly in `sigmatch` and type
instantiations.
1. `tryReadingGenericParam` no longer treats `tyCompositeTypeClass` like
a concrete type anymore, so expressions like `Foo.T` where `Foo` is a
generic type don't look for a parameter of `Foo` in non-generic code
anymore. It also doesn't generate `tyFromExpr` in non-generic code for
any generic LHS. This is to handle a very specific case in `asyncmacro`
which used `FutureVar.astToStr` where `FutureVar` is generic.
2. The `tryResolvingStaticExpr` call when matching `tyFromExpr` in
sigmatch now doesn't consider call nodes in general unresolved, only
nodes with `tyFromExpr` type, which is emitted on unresolved expressions
by increasing `c.inGenericContext`. `c.inGenericContext == 0` is also
now required to attempt instantiating `tyFromExpr`. So matching against
`tyFromExpr` in proc signatures works in general now, but I'm
speculating it depends on constant folding in `semExpr` for statics to
match against it properly.
3. `paramTypesMatch` now doesn't try to change nodes with `tyFromExpr`
type into `tyStatic` type when fitting to a static type, because it
doesn't need to, they'll be handled the same way (this was a workaround
in place of the static type instantiation changes, only one of the
fields in the #22647 test doesn't work with it).
4. `tyStatic` matching now uses `inferStaticParam` instead of just range
type matching, so `Foo[N div 2]` can infer `N` in the same way `array[N
div 2, int]` can. `inferStaticParam` also disabled itself if the
inferred static param type already had a node, but `makeStaticExpr`
generates static types with unresolved nodes, so we only disable it if
it also doesn't have a binding. This might not work very well but the
static type instantiation changes should really lower the amount of
cases where it's encountered.
5. Static types now undergo type instantiation. Previously the branch
for `tyStatic` in `semtypinst` was a no-op, now it acts similarly to
instantiating any other type with the following differences:
- Other types only need instantiation if `containsGenericType` is true,
static types also get instantiated if their value node isn't a literal
node. Ideally any value node that is "already evaluated" should be
ignored, but I'm not sure of a better way to check this, maybe if
`evalConstExpr` emitted a flag. This is purely for optimization though.
- After instantiation, `semConstExpr` is called on the value node if
`not cl.allowMetaTypes` and the type isn't literally a `static` type.
Then the type of the node is set to the base type of the static type to
deal with `semConstExpr` stripping abstract types.
We need to do this because calls like `foo(N)` where `N` is `static int`
and `foo`'s first parameter is just `int` do not generate `tyFromExpr`,
they are fully typed and so `makeStaticExpr` is called on them, giving a
static type with an unresolved node.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #23406, closes #23854, closes #23855 (test code of both compiles
but separate issue exists), refs #23432, follows #23411
In generic bodies, previously all regular `nkCall` nodes like `foo(a,
b)` were directly treated as generic statements and delayed immediately,
but other call kinds like `a.foo(b)`, `foo a, b` etc underwent
typechecking before making sure they have to be delayed, as implemented
in #22029. Since the behavior for `nkCall` was slightly buggy (as in
#23406), the behavior for all call kinds is now to call `semTypeExpr`.
However the vast majority of calls in generic bodies out there are
`nkCall`, and while there isn't a difference in the expected behavior,
this exposes many issues with the implementation started in #22029 given
how much more code uses it now. The portion of these issues that CI has
caught are fixed in this PR but it's possible there are more.
1. Deref expressions, dot expressions and calls to dot expressions now
handle and propagate `tyFromExpr`. This is most of the changes in
`semexprs`.
2. For deref expressions to work in `typeof`, a new type flag
`tfNonConstExpr` is added for `tyFromExpr` that calls `semExprWithType`
with `efInTypeof` on the expression instead of `semConstExpr`. This type
flag is set for every `tyFromExpr` type of a node that `prepareNode`
encounters, so that the node itself isn't evaluated at compile time when
just trying to get the type of the node.
3. Unresolved `static` types matching `static` parameters is now treated
the same as unresolved generic types matching `typedesc` parameters in
generic type bodies, it causes a failed match which delays the call
instantiation.
4. `typedesc` parameters now reject all types containing unresolved
generic types like `seq[T]`, not just generic param types by themselves.
(using `containsGenericType`)
5. `semgnrc` now doesn't leave generic param symbols it encounters in
generic type contexts as just identifiers, and instead turns them into
symbol nodes. Normally in generic procs, this isn't a problem since the
generic param symbols will be provided again at instantiation time (and
in fact creating symbol nodes causes issues since `seminst` doesn't
actually instantiate proc body node types).
But generic types can try to be instantiated early in `sigmatch` which
will give an undeclared identifier error when the param is not provided.
Nodes in generic types (specifically in `tyFromExpr` which should be the
only use for `semGenericStmt`) undergo full generic type instantiation
with `prepareNode`, so there is no issue of these symbols remaining as
uninstantiated generic types.
6. `prepareNode` now has more logic for which nodes to avoid
instantiating.
Subscripts and subscripts turned into calls to `[]` by `semgnrc` need to
avoid instantiating the first operand, since it may be a generic body
type like `Generic` in an expression like `Generic[int]`.
Dot expressions cannot instantiate their RHS as it may be a generic proc
symbol or even an undeclared identifier for generic param fields, but
have to instantiate their LHS, so calls and subscripts need to still
instantiate their first node if it's a dot expression.
This logic still isn't perfect and needs the same level of detail as in
`semexprs` for which nodes can be left as "untyped" for overloading/dot
exprs/subscripts to handle, but should handle the majority of cases.
Also the `efDetermineType` requirement for which calls become
`tyFromExpr` is removed and as a result `efDetermineType` is entirely
unused again.
|
|
|
|
|
|
|
|
| |
fixes #19848
Not sure why this wasn't the case already. The `if cl.allowMetaTypes:
return` line below for `tyFromExpr` [was added 10 years
ago](https://github.com/nim-lang/Nim/commit/d5798b43dec547f372eb49d5a8848a9970b12260).
Hopefully it was just negligence?
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #23977
The problem is that for *any* body of a generic declaration,
[semstmts](https://github.com/nim-lang/Nim/blob/2e4d344b43b040a4dce2c478ca13e49979e491fc/compiler/semstmts.nim#L1610-L1611)
sets the sym of its value to the generic type name, and
[semtypes](https://github.com/nim-lang/Nim/blob/2e4d344b43b040a4dce2c478ca13e49979e491fc/compiler/semtypes.nim#L2143)
just directly gives the referenced type *specifically* when the
expression is a generic body. I'm blaming `semtypes` here because it's
responsible for the type given but the exact opposite behavior
specifically written in makes me think generating an alias type here
maybe breaks something.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
refs https://github.com/nim-lang/Nim/pull/23873#discussion_r1687995060,
fixes #23386, fixes #23385, supersedes #23572
Turns the `nfOpenSym` node flag implemented in #23091 and extended in
#23102 and #23873, into a node kind `nkOpenSym` that forms a unary node
containing either `nkSym` or `nkOpenSymChoice`. Since this affects
macros working on generic proc AST, the node kind is now only generated
when the experimental switch `genericsOpenSym` is enabled, and a new
node flag `nfDisabledOpenSym` is set to the `nkSym` or `nkOpenSymChoice`
when the switch is not enabled so that we can give a warning.
Now that the experimental switch has more reasonable semantics, we
define `nimHasGenericsOpenSym2`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
actually fixes #23865 following up #23873
In the handling of `nkIdent` in `semExpr`, the compiler looks for the
closest symbol with the name and [checks the symbol
kind](https://github.com/nim-lang/Nim/blob/6126a0bf46f4e29a368b8baefea69a2bcae54e93/compiler/semexprs.nim#L3171)
to also consider the overloads if the symbol kind is overloadable. But
it treats the normally overloadable template/macro/module sym kinds the
same as non-overloadable symbols, just calling `semSym` on it. We need
to mirror this behavior in `semOpenSym`; we treat the captured symchoice
as a fresh identifier, so if the symbol we find is a
template/macro/module, we use that symbol immediately as opposed to
waiting for overloads.
|
|
|
| |
closes #21347
|
|
|
|
|
|
|
|
|
|
| |
fixes #23865
The node flag `nfOpenSym` implemented in #23091 for sym nodes is now
also implemented for open symchoices. This means the intended behavior
is still achieved when multiple overloads are in scope to be captured,
so the issue is fixed. The code for the flag is documented and moved
into a helper proc and the experimental switch is now enabled for the
compiler test suite.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #19819, fixes #23339
Since #22029 `tyFromExpr` does not match anything in overloading, so
generic bodies can know which call expressions to delay until the type
can be evaluated. However generic type invocations also run overloading
to check for generic constraints even in generic bodies. To prevent them
from failing early from the overload not matching, pretend that
`tyFromExpr` matches. This mirrors the behavior of the compiler in more
basic cases like:
```nim
type
Foo[T: int] = object
x: T
Bar[T] = object
y: Foo[T]
```
Unfortunately this case doesn't respect the constraint (#21181, some
other bugs) but `tyFromExpr` should easily use the same principle when
it does.
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #23853
Since #22610 generics turns the `Name` in the `GT.Name` expression in
the test code into a sym choice. The problem is when the compiler tries
to instantiate `GT.Name` it also instantiates the sym choice symbols.
`Name` has type `template (E: type ExtensionField)` which contains the
unresolved generic type `ExtensionField`, which the compiler mistakes as
an uninstantiated node, when it's just part of the type of the template.
The compilation of the node itself and hence overloading will handle the
instantiation of the proc, so we avoid instantiating it in `semtypinst`,
similar to how the first nodes of call nodes aren't instantiated.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #3011
In https://github.com/nim-lang/Nim/pull/23532, meta fields that defined
in the object are handled.
In this PR, RefObjectTy is handled as well:
```nim
type
Type = ref object
context: ref object
```
Ref alias won't trigger mata fields checking so there won't have
cascaded errors on `TypeBase`.
```nim
type
TypeBase = object
context: ref object
Type = ref TypeBase
context: ref object
```
|
|
|
| |
fixes #23790
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
fixes #23730
Since #23188 the compiler errors when matching a type variable to an
uninstantiated static value. However sometimes an uninstantiated static
value is given even when only a type match is being performed to the
base type of the static type, in the given issue this case is:
```nim
proc foo[T: SomeInteger](x: T): int = int(x)
proc bar(x: static int): array[foo(x), int] = discard
discard bar(123)
```
To deal with this issue we only error when matching against a type
variable constrained to `static`.
Not sure if the `q.typ.kind == tyGenericParam and
q.typ.genericConstraint == tyStatic` check is necessary, the code above
for deciding whether the variable becomes `skConst` doesn't use it.
|
|
|
|
|
|
|
|-- Entrypoint for the app. You can edit this file from within the app if
-- you're careful.
-- files that come with LÖVE; we can't edit those from within the app
utf8 = require 'utf8'
function load_file_from_source_or_save_directory(filename)
local contents = love.filesystem.read(filename)
local code, err = loadstring(contents, filename)
if code == nil then
error(err)
end
return code()
end
json = load_file_from_source_or_save_directory('json.lua')
load_file_from_source_or_save_directory('app.lua')
load_file_from_source_or_save_directory('test.lua')
load_file_from_source_or_save_directory('keychord.lua')
load_file_from_source_or_save_directory('button.lua')
-- both sides require (different parts of) the logging framework
load_file_from_source_or_save_directory('log.lua')
-- but some files we want to only load sometimes
function App.load()
log_new('session')
if love.filesystem.getInfo('config') then
Settings = json.decode(love.filesystem.read('config'))
Current_app = Settings.current_app
end
if Current_app == nil then
Current_app = 'run'
end
if Current_app == 'run' then
load_file_from_source_or_save_directory('file.lua')
load_file_from_source_or_save_directory('run.lua')
load_file_from_source_or_save_directory('edit.lua')
load_file_from_source_or_save_directory('text.lua')
load_file_from_source_or_save_directory('search.lua')
load_file_from_source_or_save_directory('select.lua')
load_file_from_source_or_save_directory('undo.lua')
load_file_from_source_or_save_directory('text_tests.lua')
load_file_from_source_or_save_directory('run_tests.lua')
elseif Current_app == 'source' then
load_file_from_source_or_save_directory('source_file.lua')
load_file_from_source_or_save_directory('source.lua')
load_file_from_source_or_save_directory('commands.lua')
load_file_from_source_or_save_directory('source_edit.lua')
load_file_from_source_or_save_directory('log_browser.lua')
load_file_from_source_or_save_directory('source_text.lua')
load_file_from_source_or_save_directory('search.lua')
load_file_from_source_or_save_directory('source_select.lua')
load_file_from_source_or_save_directory('source_undo.lua')
load_file_from_source_or_save_directory('colorize.lua')
load_file_from_source_or_save_directory('source_text_tests.lua')
load_file_from_source_or_save_directory('icons.lua')
load_file_from_source_or_save_directory('drawing.lua')
load_file_from_source_or_save_directory('geom.lua')
load_file_from_source_or_save_directory('help.lua')
load_file_from_source_or_save_directory('drawing_tests.lua')
load_file_from_source_or_save_directory('source_tests.lua')
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.initialize_globals()
if Current_app == 'run' then
run.initialize_globals()
elseif Current_app == 'source' then
source.initialize_globals()
else
assert(false, 'unknown app "'..Current_app..'"')
end
-- for hysteresis in a few places
Current_time = 0
Last_focus_time = 0 -- https://love2d.org/forums/viewtopic.php?p=249700
Last_resize_time = 0
end
function App.initialize(arg)
if Current_app == 'run' then
run.initialize(arg)
elseif Current_app == 'source' then
source.initialize(arg)
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.resize(w,h)
if Current_app == 'run' then
if run.resize then run.resize(w,h) end
elseif Current_app == 'source' then
if source.resize then source.resize(w,h) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
Last_resize_time = Current_time
end
function App.filedropped(file)
if Current_app == 'run' then
if run.filedropped then run.filedropped(file) end
elseif Current_app == 'source' then
if source.filedropped then source.filedropped(file) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.focus(in_focus)
if in_focus then
Last_focus_time = Current_time
end
if Current_app == 'run' then
if run.focus then run.focus(in_focus) end
elseif Current_app == 'source' then
if source.focus then source.focus(in_focus) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.draw()
if Current_app == 'run' then
run.draw()
elseif Current_app == 'source' then
source.draw()
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.update(dt)
Current_time = Current_time + dt
-- some hysteresis while resizing
if Current_time < Last_resize_time + 0.1 then
return
end
--
if Current_app == 'run' then
run.update(dt)
elseif Current_app == 'source' then
source.update(dt)
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.keychord_pressed(chord, key)
-- ignore events for some time after window in focus (mostly alt-tab)
if Current_time < Last_focus_time + 0.01 then
return
end
--
if chord == 'C-e' then
-- carefully save settings
if Current_app == 'run' then
local source_settings = Settings.source
Settings = run.settings()
Settings.source = source_settings
if run.quit then run.quit() end
Current_app = 'source'
elseif Current_app == 'source' then
Settings.source = source.settings()
if source.quit then source.quit() end
Current_app = 'run'
else
assert(false, 'unknown app "'..Current_app..'"')
end
Settings.current_app = Current_app
love.filesystem.write('config', json.encode(Settings))
-- reboot
load_file_from_source_or_save_directory('main.lua')
App.undo_initialize()
App.run_tests_and_initialize()
return
end
if Current_app == 'run' then
if run.keychord_pressed then run.keychord_pressed(chord, key) end
elseif Current_app == 'source' then
if source.keychord_pressed then source.keychord_pressed(chord, key) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.textinput(t)
-- ignore events for some time after window in focus (mostly alt-tab)
if Current_time < Last_focus_time + 0.01 then
return
end
--
if Current_app == 'run' then
if run.textinput then run.textinput(t) end
elseif Current_app == 'source' then
if source.textinput then source.textinput(t) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.keyreleased(chord, key)
-- ignore events for some time after window in focus (mostly alt-tab)
if Current_time < Last_focus_time + 0.01 then
return
end
--
if Current_app == 'run' then
if run.key_released then run.key_released(chord, key) end
elseif Current_app == 'source' then
if source.key_released then source.key_released(chord, key) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.mousepressed(x,y, mouse_button)
--? print('mouse press', x,y)
if Current_app == 'run' then
if run.mouse_pressed then run.mouse_pressed(x,y, mouse_button) end
elseif Current_app == 'source' then
if source.mouse_pressed then source.mouse_pressed(x,y, mouse_button) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function App.mousereleased(x,y, mouse_button)
if Current_app == 'run' then
if run.mouse_released then run.mouse_released(x,y, mouse_button) end
elseif Current_app == 'source' then
if source.mouse_released then source.mouse_released(x,y, mouse_button) end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
function love.quit()
if Current_app == 'run' then
local source_settings = Settings.source
Settings = run.settings()
Settings.source = source_settings
else
Settings.source = source.settings()
end
Settings.current_app = Current_app
love.filesystem.write('config', json.encode(Settings))
if Current_app == 'run' then
if run.quit then run.quit() end
elseif Current_app == 'source' then
if source.quit then source.quit() end
else
assert(false, 'unknown app "'..Current_app..'"')
end
end
|