-
Notifications
You must be signed in to change notification settings - Fork 6
Type hinting #116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Type hinting #116
Conversation
I guess the automated test kind of got stuck. |
Seems I caused some memory leak issue in tests which I have to fix in PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall awesome PR! This is something I've been trying to get around to for a while so thanks for getting in there and getting it in there. Did some testing and it all appeared to work as expected which is really exciting! I've some comments most of them are very minor, most minor cleanup things. Feel free to ignore most of them.
If you could I would write a test or two in typeChecker.spec.ts
specifically for your new type hints. Maybe something as simple as
function example { #returns: string
parameter x // #type: string
return x.
}
Then just check that example has one parameter of string
that returns string
. Again big thanks this is the first PR against the repo that wasn't from me. So any feedback for me on how the code is organize would be appreciated as well!
@@ -57,6 +60,10 @@ export class Token implements TokenBase { | |||
}; | |||
} | |||
|
|||
public get getRef(): Token | undefined { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably don't need this getter here since this.ref
already is available.
@@ -103,13 +103,15 @@ export class Var extends Decl { | |||
public readonly toIs: Token; | |||
public readonly value: IExpr; | |||
public readonly scope: Scope; | |||
public readonly typeHint?: ITypeHint; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're applying typeHint
to each declaration type I think we should move it up to the Decl
super class and just pass typeHint
into super in each case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started with only parameters.
Later added other ones.
constructor is using builder for subclasses, but not for superclass. Seems to be few issues just moving to to superclass.
@@ -75,6 +79,9 @@ export class Parser { | |||
this.logger.info( | |||
`Parsing started for ${file} with ${this.tokens.length} tokens.`, | |||
); | |||
this.logger.info( | |||
`Parsing started for ${file} with ${this.types.length} tokens.`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we could have something like Parsing started for ${file} with ${this.types.length} type hints.
@@ -114,6 +120,17 @@ export class Scanner { | |||
kind: ScanKind.Token, | |||
}; | |||
|
|||
this.typeResult = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be good to make a helper function now that we have three of these now. Maybe something like
private createTokenResult(tokenType: TokenType, scanKind: ScanKind): TypeResult {
// ...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not actually even sure why do we need that placeholder declaration here.
I just made a copy without going much into details why it has to be in here. They all seem like placeholders which we should not need to create.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your right, I'm not sure why it's there either as it seems we assign it then just return it in the various methods. My guess was there was some idea I half implemented then abandoned. I would say feel free to remove these and update the token generation methods. If not I can do some cleanup in a different PR
case ScanKind.Type: | ||
// Were not able to reference earlier to lookback | ||
// Gets first identifier on the line where type is defined | ||
result.result.ref = tokens.filter(token => token.type === TokenType.identifier && token.start.line == result.result.start.line)[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine but we might want to think about some like a line buffer so we're searching through fewer tokens. I did pull down your branch and it doesn't appear issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems as long as the array we are searching from (token array) does not grow too big, then it should not be a problem
https://github.com/dg92/Performance-Analysis-JS
On my biggest script I have token array of around 6000 elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we just find
instead of filter with index 0. I think its more clear that we're just looking for one element plus in the repo you linked to shows better performance so that seems like a win win.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeap, changed it to find.
@@ -182,7 +197,11 @@ export class TypeChecker | |||
const { tracker } = decl.identifier; | |||
|
|||
if (this.isBasicTracker(tracker)) { | |||
tracker.declareType(result.type.assignmentType()); | |||
if (decl.typeHint && TypeHint.get(decl.typeHint.toString())) { | |||
tracker.declareType(TypeHint.get(decl.typeHint.toString()) || result.type.assignmentType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a future PR we could try to report an warning if the typehint isn't assignable to the inferred type. Maybe something like this
local x is "some string" // #type: scalar
// ^--- could probably indicate that scalar isn't assignable to string
local y is ship:parts[0] // #type: engine
// ^-- engine is a part so this should be ok
The common case will be structure since that when the type checker gives up which anything should be assignable to it
} | ||
|
||
|
||
// debug code: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would probably be good to clean this up
@@ -736,7 +736,7 @@ describe('Statements', () => { | |||
const [first, second] = params; | |||
|
|||
expect(first).toBe(structureType); | |||
expect(second).toBe(createUnion(true, structureType, noneType)); | |||
expect(second).toBe(createUnion(true, integerType, noneType)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💥
@@ -242,9 +249,10 @@ export class Parser { | |||
} | |||
|
|||
// parse function declaration | |||
private declareFunction(scope?: Decl.Scope): INodeResult<Decl.Func> { | |||
private declareFunction(scope?: Decl.Scope, typeHint?: Maybe<ITypeHint>): INodeResult<Decl.Func> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't appear anything is passed for typeHint
here or in the other declare
functions below. I think it would be good to remove them for now if this was temporary code.
@@ -32,6 +34,7 @@ export class Token implements TokenBase { | |||
this.start = start; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we only set this in the scanner I think we should just update the constructor and make ref
readonly
constructor(
type: TokenType,
lexeme: string,
literal: any,
start: Position,
end: Position,
uri: string,
ref?: Token) {
// ...
}
I was thinking of doing couple of tests yes, It needs some improvements, I found out some more issues with it. One more thing I remembered now that is missing is dynamic list generation, Parser needs improvements. I think I have defined 2-3 lists, Type hints are currently defined in one file and it is unknown what are the types they can use, Maybe autocomplete could be used to give some hints on what kind of type definitions there are. Will look into writing some tests and improving code. |
It seems the parser config used in tests is not defining types. Have to figure out why it is ignoring all the hints. |
To your first comment, I think an autocomplete would definitely be a good thing to have. I think it would probably make sense to add it in a separate PR. For type hinting depending on which spec file your in you may need to add |
Did struggle with testing. Not sure if i can make it work. During the test, it has not done the visitParameter it seems, so it has the type declared as structure and not picking up the updated one. That is the reason why it crashes for me. Function on the other hand had parameter types defined already, which is odd. So it needs some more figuring out how I can force the token types to be defined by the time the test takes place. It seems if I run it 2 times before test checks, everything is in order. Wanted to check the order it gets parameters-variables and such declared. Hoping I will find a way to make sure it is in order that will also not make it lag with bigger scripts. For longer script I put my whole launch, orbit, rendezvous and deorbit and recovery with last part as suicide burn, while doing science collection and transmission as one big script. Basically took all the libraries and stitched it as one file to put language server to the test. |
Just as a point of reference I typically gauge speed against Feel free to push code to your PR if you need another set of eyes on it. I can take a stab at writing some tests if you need some help. Definitely very interested in getting this merged! |
What kind of change does this PR introduce?
I made quite few changes like type hinting
// #type: vesselTarget
over the weekend.PR adds typehinting with syntax
// #type: typestring
and// #returns: typestring
It also takes the parameters for functions to generate function signature.
There is some residue code from testing out:
Noticed that I had to do 2 passes over the whole code, because kos allows to have functions defined after calling them, but the code currently would give warning about signature not found.
The code is not the prettiest and not the cleanest, but could be useful.
What is the current behavior? (You can also link to an open issue here)
No possibility of code hinting
What is the new behavior (if this is a feature change)?
Possibility of code hinting
Problems/Limitation:
Currently the limitation is that 1 parameter per line, if there are more, then it still takes first typestring and applies it to first parameter in the line.
There are some issues still. on function you can have
// #type: typestring
and// #returns: typestring
. Both work the same. Needs way to differentiate between 2 of them.Type-hinting at the moment only lets you to enter only one type. Not possible to make it something like
exist()
command has (string or path)Probably should change the syntax to work more like
// #parametername: typestring
for parameters.Other information:
Some changes in suffixes and such that came out as problems when testing code. Probably another PR.