#724 Imports completion questions (type database)

tcolar Wed 19 Aug 2009

I started working on auto-completion in my Netbeans plugin. I'm starting with import completion.

Typically with NB this is done by indexing packages(pods) & types in source folders and/or libs folder.

I could do that with Fan (at least for the sources anyway), for libraries not sure yet what is the proper way to do it(reflection probably - will look how they do it for java) ... but I think I remember that Fan has it's own "Type database" ... would it make sense for me to use that instead of probably slower parsing/indexing ? Also does that "DB" contain all of the pods/types in the repo or just the ones that where "loaded".

If you think it makes sense to use the DB, any info on using it ?

Thanks.

brian Wed 19 Aug 2009

I think the simplest solution is to break things up into:

  • current pod being compiled
  • external pods

I am not sure how Java IDEs work, but I assume they "index" the entire project from source. I would expect that in a Fan IDE, only the current pod would be indexed by source, and external pods would use the pre-compiled pod files via reflection. In this model the IDE works like the compiler does - the compiler does type checking against pods, not source. So if you want to auto-completion for a new method in an external pod, then that pod needs to be compiled first. The seems to simplify the problem and should make for more efficient IDEs - they only need to index the current pod being worked on (by index I mean build an AST model).

To use reflection in the IDE, the simplest thing is to just use "sys.jar" and its Java implementation of the sys APIs. For example to call Pod.find, you can use the Java class fan.sys.Pod. Using Fan APIs from Java is really straight forward. Just ensure that you set the "fan.home" system property before you load any classes from "sys.jar". Then everything is booted up in the static initializer for fan.sys.Sys.

What won't work today is reloading a pod which has been recompiled. But you might be able to handle this yourself in brute force fashion by flushing the classloader used to load sys, and then booting Fan up again.

You might also what to check out what Cheeser did for tradewinds since he is hosting a Fan runtime from Java.

tcolar Wed 19 Aug 2009

Right, compiled POds through type DB should be faster/more convenient than parsing. I would only parse sources for currently opened pods in the IDE most likely.

I'll investigate some more the reflection and see how it goes, if I can get it to work without needing a runtime that would be nice.

Just ensure that you set the "fan.home" system property before you load any classes > from "sys.jar". Then everything is booted up in the static initializer for fan.sys.Sys.

All see how that goes, I seem to remember that you could not change/define on the fly Java system properties, so I might have to spawn another VM / Fan runtime anyway.

What won't work today is reloading a pod which has been recompiled. But you might be > able to handle this yourself in brute force fashion by flushing the classloader used > to load sys, and then booting Fan up again.

The type database wiki page (which I can't seem to find anymore) said something along the line of "It will update change pods/types automatically at runtime", but i guess from what you just said that it does so only at BOOT time, right ?

Thanks.

brian Wed 19 Aug 2009

I seem to remember that you could not change/define on the fly Java system properties

Yeap you can. Just need to do it before you load the Sys class.

The type database wiki page (which I can't seem to find anymore) said something along the line

Technically the typedb is one piece of the overall reflection subsystem. What you need and will be using isn't the typedb per se, but rather just the reflection APIs to Pod/Type/Slot definitions.

Although if you want, you might just drop down a level and instead of using the reflection APIs, use the code in fanx.fcode to parse the type/slot meta-data straight out the pod file and manage it yourself. This is basically what Pod/Type/Slot are doing - just building an in-memory model of the fcode and then eventually mapping it to Java classes. But note, just using reflection doesn't actually emit Java classes - this is one of the cool things about Fan, you can do reflection before the Java classes are actually emitted.

tcolar Wed 19 Aug 2009

Sounds good, that should get me what I need.

I might go the fanx.fcode route, because it might make it easier to "reload" pod that changed (timestamp)

tcolar Wed 19 Aug 2009

Sorry, I forgot to ask this: Does the reflection API have access to the doc(fandoc), if not I might go back to using the sources, because it's definitely nice to have the fandoc to go along with the completion proposals.

Update: I guess FDoc in fanx is the answer.

brian Wed 19 Aug 2009

Sorry, I forgot to ask this: Does the reflection API have access to the doc(fandoc),

Yes, in Fan this is super simple:

fansh> Str#get.doc
Get the character at the zero based index as a Unicode code point.
Negative indexes may be used to access from the end of the string.
This method is accessed via the [] operator.

In the pod it is stored in a very simple text file. See fanx.fcode.FDoc and the impls for Pod.doc and Type.doc

tcolar Fri 4 Sep 2009

It would be nice if Pod.fan had a reload() method that would do the same as load() but not prevent loading if already in the cache(lazy cache).

I can't seem to extend/ use Pod to make it reload updated/new pods because of this and the fact much of it is not public.

I guess I have to use FPod for now.

Also i find this a bit weird:

fpod = new FPod(null, null, null);
fpod.readFully(new ZipInputStream(SysInStream.java(in)));

shouldn't readFully() rather be a static method that returns an FPod.

brian Fri 4 Sep 2009

The problem is that it would introduce an extremely confusing issue of what to do with existing instances and classes using the old Pod definition.

Reloading scripts is one thing (and we already do it), because each version of the pod gets its own unique pod name.

But well-defined pod modules don't work that way.

tcolar Fri 4 Sep 2009

OK. I should be alright with FPod for my purpose.

tcolar Thu 10 Sep 2009

I started working on completion. I have imports completion working now, and it shows the relevant "fandoc" for the selected pod/type in the completion windows which is nice.

The thing though is that Pod.doc & Type.doc returns the doc as plain text. For some detailed(syntax heavy) doc such as sys:Buff or sys::Depend, that looks quite ugly.

So anyway my question is:

Is there a method somewhere to parse the doc into HTML? I know Sidewalk does this, but is that code in the Fan distro.

I don't mind the Fandoc Syntax being the same as used in the fandev Wiki, but if the code to parse fandocs is not available in the fan distro, this is not useful / usable.

Thanks.

andy Thu 10 Sep 2009

There's an example in docLib.

tcolar Thu 10 Sep 2009

Thanks, i missed that somehow.

tcolar Thu 10 Sep 2009

One thing is that i need to run this from java, since it's a pure Fan library I can't call it directly like I do with fan classes implemented in Java(such as fanx.fcode.FPod).

I have access to the fan user fan distro, so I guess I can "use" it

One way would be for me to do something like call a little fan script that return the doc like: java fanx.tools.Fan fanIde.getDoc sys type (with getDoc using FandocParser as in your example), it would work, but this seem quite inefficient (spawning vm's) and limited.

I'm thinking maybe I could spawn One Fan shell process and interact with that, but I'm not sure if it's "capable" enough to return me what I need?

Any suggestion on a preferred way to do something like this (a.k.a using a Fan lib from Java) ?

andy Fri 11 Sep 2009

This is just a classloader issue, which I'm not sure we ever really cemented a design for. Brian can correct me, but I believe if you just use FanClassLoader to load the fandoc class into the same VM - then just use it directly - it will work. Thats not the long-term solution here though.

brian Fri 11 Sep 2009

@tcolar,

Since you already linking against sys.jar, I would suggest just using reflection to convert from fandoc to HTML. It is pretty "simple" using this code:

String fandoc = "you want to do *what*!";
FanObj parser = (FanObj)Type.find("fandoc::FandocParser").make();
FanObj doc    = (FanObj)parser.type().method("parseStr").call(parser, fandoc);
String html   = (String)doc.type().method("write").call(doc, Type.find("fandoc::HtmlDocWriter").make());
System.out.println(html);

Don't you wish Java had a -> operator!

tcolar Sat 12 Sep 2009

Ha cool, the FanObj casting was the trick i was missing. Thanks. Same code in Fan would be so much shorter/cleaner !

tcolar Wed 23 Sep 2009

I'm trying to use this but I keep getting an error:

sys::Err: java.lang.NoClassDefFoundError: Could not initialize class fan.fandoc.LineType
        at fan.sys.Err.make(Err.java:43)
        at fan.sys.Method.invoke(Method.java:550)
        at fan.sys.Method$MethodFunc.call(Method.java:267)
        at fan.sys.Method.call(Method.java:151)
        at net.colar.netbeans.fan.indexer.FanPodIndexer.fanDocToHtml(FanPodIndexer.java:168)
        at net.colar.netbeans.fan.indexer.FanPodIndexer.getPodTypeDoc(FanPodIndexer.java:148)
        at net.colar.netbeans.fan.completion.FanCompletionHandler.document(FanCompletionHandler.java:95)

It errors out at this line:

FanObj doc    = (FanObj)parser.type().method("parseStr").call(parser, fandoc);

sys.jar is loaded, so I'm not surer what's going on ?

Any clue ?

brian Wed 23 Sep 2009

Any clue ?

Not really.

I tried that our snippet of Java code inside sys.jar and it worked.

might want to be some debug into FanClassLoader - something must be failing there

tcolar Wed 23 Sep 2009

I think I found what the deal was, i have two versions of fan, since at some point i was trying to build fan itself, I think this is where there is a mixup, though i'm not sure exactly how yet.

tcolar Fri 25 Sep 2009

Nevermind it still fails, I notice there is one error very first before the LineType one.

Preamble:sys::
sys::Err: java.lang.NoSuchMethodError: fan.sys.FanObj.toImmutable(Ljava/lang/Object;)Ljava/lang/Object;
        at fan.sys.Err.make(Err.java:43)
        at fan.sys.Method.invoke(Method.java:550)
        at fan.sys.Method$MethodFunc.call(Method.java:267)
        at fan.sys.Method.call(Method.java:151)
        at net.colar.netbeans.fan.indexer.FanPodIndexer.fanDocToHtml(FanPodIndexer.java:168)
....

Does it make any more sense ?

KevinKelley Fri 25 Sep 2009

toImmutable was one of the very recent changes, so it looks a lot like you've got some out of sync code somewhere. Using a sys.jar that's out of date?

tcolar Fri 25 Sep 2009

Ok, I'll check that out. I don't think I do use another one, but maybe I put sys.jar in my classpath at some point for debugging or something.

tcolar Fri 25 Sep 2009

OK< i figures out the issue with sys.jar ... now i have a new weirdness.

Using same code here provided by Brian:

String fandoc = "you want to do *what*!";
FanObj parser = (FanObj)Type.find("fandoc::FandocParser").make();
FanObj doc    = (FanObj)parser.type().method("parseStr").call(parser, fandoc);
String html   = (String)doc.type().method("write").call(doc, Type.find("fandoc::HtmlDocWriter").make());
System.out.println(html);

I do get the html printed out to stdout(presumably by the write method ?), but the html var is null ...

Maybe I'm just tired.

andy Sat 26 Sep 2009

HtmlDocWriter.make takes an OutStream as an arg with a default value of Sys.out, so pass in a Buf to capture the output in memory.

tcolar Mon 28 Sep 2009

Sorry for being thick, but i can't quite find something that work

FanObj parser = (FanObj) Type.find("fandoc::FandocParser").make();
FanObj doc = (FanObj) parser.type().method("parseStr").call(parser, fandoc);
Buf buf = Buf.make();
List params = (List) new List(buf.type(),1);
params.add(buf);
//FanObj writer = (FanObj) Type.find("fandoc::HtmlDocWriter").method("make").call(buf);
FanObj writer = (FanObj) Type.find("fandoc::HtmlDocWriter").make(params);
doc.type().method("write").call(doc, writer);
html = buf.toStr();

Can't seem to find the proper way to pass a Buf to HtmlDocWriter.make, have tried many different things, but keep getting errors like:

sys::ArgErr: java.lang.IllegalArgumentException: argument type mismatch
        at fan.sys.ArgErr.<init>(ArgErr.java:47)
        at fan.sys.Err.make(Err.java:39)

andy Mon 28 Sep 2009

Just need to call out on Buf - try this:

FanObj writer = (FanObj)Type.find("fandoc::HtmlDocWriter")
  .method("make").call(buf.out());  
doc.type().method("write").call(doc, writer);
html = buf.flip().readAllStr();

tcolar Mon 28 Sep 2009

Doh! Thanks, should have read the fandoc more carefully. As an aside, it would be nice if IllegalArgumentException exception was more detailed: Ex: Expected Outpustream but got Buf instead :)

brian Mon 28 Sep 2009

As an aside, it would be nice if IllegalArgumentException exception was more detailed: Ex: Expected Outpustream but got Buf instead

Fan doesn't generate that exception - it is raised by Java reflection and we just wrap it as ArgErr

tcolar Mon 28 Sep 2009

Ha, true, too bad.

tcolar Fri 20 Nov 2009

Ok, I'm working on this once again. I've decided to use the FPod facilities as the base for the completion, this proves to be very easy and practical!

Netbeans usually works fully from sources ... but working from sources is pretty difficult especially with a language like Fan with implicit casting, and inferred types etc. Also the indexing Netbeans uses, based on Lucene, appears buggy and doesn't seem to me like a very good solution for the completion job either.

Anyway, now the trick to this, is the code that's being "currently" worked on: It's not compiled yet, and potentially not compilable yet (incomplete code) ... so I still have to analyze the source for those, what I was wondering is whether there are built-in facilities in Fan that could help me .. say for example something to help me figure out and inferred variable Type and things like that.

I know Andy mentioned also working on some AST builder from fan source and was wondering if some of that might help me or if you have other suggestion on good ways to implement this (Since fan always seem to have some very nice built-in feature i miss at first)

If not, I'm going to start from my ANTLR built AST but will have to figure out lots of other things to get it to work right (like trying to figure out an inferred type).

Thanks.

brian Sat 21 Nov 2009

Well the simplest way would be use the fcode from the last successful compile. That obviously wouldn't pick up changes during the current cycle, but I expect would be a pretty good compromise - its drop dead simple. This was the approach I was considering for Flux.

If you really want to work against latest working version of the pod, you can try using the Fantom compiler, but it wasn't really designed as an IDE compiler with error correction, etc (something which we've discussed a lot previously).

andy Sat 21 Nov 2009

I used the normal Fantom compiler for the AST viewer, so its only useful if the code actually compiles, so not sure thats what you want.

tcolar Sat 21 Nov 2009

Ok, that's what I thought.

Using FPod is very easy, the trickier part right now is figuring out the Type the completion is being called on ... since it's in an uncompiled(and probably uncompilable at that moment) source. (example : amethod().somefield._)

Anyway I'm currently working on using ANTLR to try and figure out the termExpr / call chain and use that to try to determine the type, that's by far the hardest part.

tcolar Tue 24 Nov 2009

Wanted to report a minor annoyance:

When using fpod.readFully(new ZipInputStream(fos)) it system.out LOTS of entries like this:

WARNING: unexpected file in pod: typedb.def
WARNING: unexpected file in pod: doc/ChangeLog.fandoc
WARNING: unexpected file in pod: doc/Faq.fandoc
WARNING: unexpected file in pod: x256/flux.png

brian Tue 24 Nov 2009

@tcolar

Those are valid warnings because you probably don't want to be using readFully(ZipInputStream). That method is designed to read a script pod from memory where we are not expecting resource files to be in the stream.

Try using the constructor with a ZipFile and use readFully() and see if that works better for you.

tcolar Wed 25 Nov 2009

Thanks, works good.

tcolar Wed 25 Nov 2009

Did good progress on the completion. One more question, I love Fan featured to ask for existing Pod/Types.

Is there a similar easy built-in way to ask for the available Java types (JVM and lib/java/ext jars) ... otherwise i suppose i might be able to do it through the classloader.

brian Wed 25 Nov 2009

Java doesn't provide much in the way of jar/package/class reflection.

You can take a look at a compilerJava::ClassPath which is a hack I wrote to deal with the issue for Java FFI.

tcolar Thu 26 Nov 2009

Cool, I was able to use it directly this way:

FanObj classPath = (FanObj) Type.find("compilerJava::ClassPath").method("makeForCurrent").call();
Map map = (Map)classPath.type().field("classes").get(classPath);
String[] packages=(String[])map.keys().asArray(String.class);
for(String pack : packages)
{
	String[] classes=(String[])((List)map.get(pack)).asArray(String.class);
    // for real use I would store in a Java Map, but to test just system.out all
	for(String clazz: classes)
		System.out.println(pack+"."+clazz);
}

It's a bit ugly(casting) but it does the job.

Does that seem right or could I clean this a bit ?

brian Thu 26 Nov 2009

Does that seem right or could I clean this a bit ?

Looks as right as you can make ugly Java reflection code.

tcolar Sat 5 Dec 2009

For some of my Type determination stuff, I want to get the "Type" of a List of objects.

For example I can do this (java) fan.sys.Type listType = new fan.sys.List(resolved.getAsFanType(),0).type();

Where resolved.getAsFanType() is a FanType for example Str, so I want to get a Fan Type of type List of Str (Str[])

The line of code listed above works, but itt creates an actual list just to gets it's type, I'm sure I should be able to avoid to create an object to get a type, but I'm not sure about the syntax to do it.

maybe a Type.make("Str[]") or ListType.make(Str')' or something ?

Thanks.

tcolar Sat 5 Dec 2009

Nevermind jut found Type.toListOf()

tcolar Sat 5 Dec 2009

Ok, that works great for fan List of Fa objects (Fan)Type.toListOf()

Now I also need to deal with a Fan List of Java object (ex: String[]).... how do I get a Type for that ??

Can I somehow get a Fan Type from a Java Type and then call Type.toListOf() or is there something else.

I have the same questions for Map.

Need to do that kind of stuff for completion purposes (hard) in this case i want to know a Field /var Type, so I can later propose it's slot.

Thanks.

brian Sat 5 Dec 2009

Not quite sure what you are trying to convert from and to, if you are trying to map java Classes to Fan types, then use fanx.util.FanUtil.toFanType (or its other utils):

FanUtil.toFanType(String.class).toListOf()

tcolar Sat 5 Dec 2009

I guess I wasn't too clear.

What I'm trying to do at this point is looking for fields defined in the source code and store their name and type (say Int myvar).

So later if the user try to complete that field (myvar._) i can propose appropriate slots for that field type.

I got this working fine for simple types right now (both fan & ffi) as well as for List of Fan objects. (using either Fan's Type.find or Java reflection)

Now if I have a List or map of java objects such as:

using [java] com.corp.MyCoolClass
....
MyCoolClass[] listOfCoolClass
Str:MyCoolClass mapOfCoolClass

I'm able to resolve MyCoolClass as my java type just fine, but what i want is a Fan type (since it's a fan list) ... I expect this to be a Fan type representation of list of MyCoolClass.

This way when a user try to complete listOfCoolClass[0]._ I can propose the appropriate fields/methods of MyCoolClass.

I'll look into toFanType() but if I do toFanType(MyCoolClass.class) will I be able to reflect the Java methods/slots behind it ?

If yes then cool, I would use toFanType on my java types right away and be able to use Fan's reflection and not have to deal with java reflection much... if not then I still need help figuring out how to get the List of Java objects type.

Hope this is clear.

tcolar Sat 5 Dec 2009

Also as a somewhat related question, could fantom.org somewhere have fandoc/javdoc for the java code of the Fan distribution. (and maybe other non-api stuff as well)

It's fairly well documented, but to look at it I have to dig through the source code, kinda slow and inefficient.

brian Sat 5 Dec 2009

I don't think I am quite following you.

If you want the type of the items in the list, it is just sys::List.of.

Also might want take a look at sys::Type.params

But I suspect you might be heading down the wrong path. Consider that in the expr foo[bar], the resulting type doesn't have anything to do with List or Map, but is really just sugar for foo.get.

So if foo is Str[] and you parameterize get then you know foo[bar] evaluates to a Str.

So put another way, I think you will find it easier to break problems up:

foo[bar]      // when you see this
foo.get(bar)  // evaluate the type as this

Then you see a generic method you just need to substitue V, K, L, or M.

Make sense?

tcolar Sat 5 Dec 2009

Ok i see FanUtil.toFanType(String.class) will give me a JavaType for a java class and that supports reflection so it's actually gonna be nice.

I was trying to carry a java type for java fields and a fan type for fan fields, but that's unnecessary I can always use fan's Type.

Cool, should make my code cleaner too.

As for your previous answer ... all i was trying to say is that when somebody tries to complete

someList._ i want to propose List slots while for:

somelist[0]._ i want to propose the List contained type slots ... point being I need to keep track of the contained type and was confused on how to do that for a java type ... but now that I see FanUtil.toFanType(String.class) can handle Java types and allow reflection I don't have a problem.

Anyway if that's still not clear, that's OK, you got me the info I needed :)

tcolar Sun 6 Dec 2009

I now have slots proposal working.

I'm trying to make it look nice and doing fine.

One small questions. Slot as a method hasDefault() .. i'd like to show the default value in the proposal ... but there isn't any getDefault() ... any way to get to it ?

Thanks.

brian Sun 6 Dec 2009

If the docs are available in the pod, you can get the string used by the fandoc:

fansh> Int#fromStr.doc
@checked.def=true
@radix.def=10
Parse a Str into a Int using the specified radix.
If invalid format and checked is false return null,
otherwise throw ParseErr.

See docCompiler for how to parse it.

KevinKelley Sun 6 Dec 2009

I remember noticing those @.defs in the fandoc some time back, and thinking it meant they were being put on the slot as facets; quick check of slot.facets showed they're not. But it seemed like it might make sense to do that; is there a reason for/against? It'd obviously be useful for tools.

brian Mon 7 Dec 2009

I remember noticing those @.defs in the fandoc some time back, and thinking it meant they were being put on the slot as facets

The general problem with docs as facets is that it can be really big and it requires an extra compilation phase, which is why I made it something separate.

You could argue that defs aren't just doc things, but I think since we are saving a doc string and not the actual expression, I definitely think of them as doc-only things. Although maybe some of this latest IDE work might shift that opinion.

tcolar Tue 8 Dec 2009

I'm doing completion(good progress) and proposing the Slots for a given type to the user. This is working well, for example

When I show the returned Type, for example for a field I use: field.of() , for a method / constructor I use method.returns()

This work well for field/method

Now to the question: Method.returns for a constructor (make) is Void .... is this normal ? I would expect it to return the slot type, rather than void.

brian Tue 8 Dec 2009

Now to the question: Method.returns for a constructor (make) is Void .... is this normal ? I would expect it to return the slot type, rather than void.

It is just part of constructors dual nature. On the outside they are static and return their declaring type. On the inside they are instance and void. As a general rule constructors override Slot methods for their inside behavior.

tcolar Wed 9 Dec 2009

Another question: Are there any docs on what constitutes a scope in Fan (as far as variables scoping go), once in a method, is a scope always delimited by { and } ... or are there some other things that could be a scope ? - can't think of any right now, since itBlocks, closures etc.. all use {}.

Thanks.

brian Thu 10 Dec 2009

nce in a method, is a scope always delimited by { and } ... or are there some other things that could be a scope

Pretty much like you'd expect - {} introduce a scope whether they are an if/else, for, while, try statement or an it-block/closure. Binding occurs in order of:

  1. local variable or parameter
  2. slots on implicit it
  3. slots on implicit this

Also remember that like Java a for loop introduces a scope in its initializer expression:

for (i:=0; i<10; ++i) {...}

jodastephen Thu 10 Dec 2009

Also remember that like Java a for loop introduces a scope in its initializer expression:

Do we need to have this additional rule? Could we simplify by saying that there is no new scope for i and if there is a conflict then the loop variable is a duplicate and an error. Seems like a useful simplification (the less rules the better).

brian Thu 10 Dec 2009

Do we need to have this additional rule?

Considering that 99% of for loops use that technique it would really suck :-) I think it is something that programmers take for granted, but compiler writers just need to be aware of. BTW, this case also presents itself with catch clauses:

try
{
}
catch (Err e)
{
}

The e is introduced into the catch block scope only (but actually isn't really declared there).

helium Thu 10 Dec 2009

Do we need to have this additional rule?

YES!

tcolar Thu 10 Dec 2009

Yeah and then there is try/catch without brackets to deal with as well. Anyhow, I'm probably going to release a version with completion within a week.

It will probably not handle EVERYTHING then, such as namedSuper, cast vars, it, for/try variables, but some completion is still way more useful than none so it's still good to start with.

BTW: In Fan I very rarely use a for loop yet, I much prefer 5.times{} or .each type loops etc...

Also loop vars are usually called something like i or j, and exception e, so completion really is not a big deal here, though I'll deal with it at some point, maybe not right away.

Anyway at this point I got completion working for

  • using statement, both java FFI and Fan (pod & type)
  • Fields and scoped variables, method parameters etc... which is already quite useful.

Still working on some things I want to have (such as super._ completion) and some aspects of inferred variables.

The nice thing is it shows the Fan/Slot fandoc along with the proposals, that's probably what I use completion for, just as much as saving some typing, especially for Fan where I'm not that used to the libraries yet.

tcolar Fri 11 Dec 2009

Since JavaType.pod() returns null(expected), it would be nice if JavaType.podname was public or if there was a JavaType.getPodName() method.

Right now it seem to gte the java type podname, I have to get qname() and strip the type of.

brian Sat 12 Dec 2009

it would be nice if JavaType.podname was public or if there was a JavaType.getPodName() method.

added JavaType.podName() - changeset

tcolar Thu 17 Dec 2009

Another thing about Fantom's JavaType:

When I have a Java object that has a field marked transient, I can't seem to find that out from JavaType.

JavaType has no method isTransient(), alternatively maybe it should set a "transient" Facet on the JavaType if the underlaying Java field is transient.

It does not seem to be either, so I can't seem to know whether it's transient or not.

brian Thu 17 Dec 2009

I pushed a fix - try that out

tcolar Thu 17 Dec 2009

Thanks.

tcolar Tue 29 Dec 2009

Couple more question. (Working on indexing compiled pods now), source done.

Q1: Given a FType I find the attributes such as: isOverride = hasFlag(ftype.flags, FConst.Override) // does a binary &

That works fine, except for "readonly" ... doesn't exist in FConst ... is it just missing(hardcoded) or is readonly maybe not a modifier(flag) but a facet or something ?

Q2: Given the FType, how do I figure whether it's a class, mixin or enum ? Maybe type.base or type.self are flags (unsure) ? or maybe in FTypeRef ?

I'd like to do it just with the (fanx.FType) I mean without using fan.Type.find() since I believe that works only with pre-cached pods that where present when fan runtime was started, in my case here i need to check new pods on the fly, which I do using fpod.readFully() Let me know If I'm wrong on that.

Thanks.

tcolar Tue 29 Dec 2009

nevermind about Q1 (readonly), only need answer to Q2.

brian Tue 29 Dec 2009

  • readonly is just sugar for private setter
  • FType.flags has flags both mixin and enum (lack of either flag means normal class)

tcolar Tue 9 Mar 2010

As I'm going to work some more on type resolution / semantic analysis, I'd like to know where I can find info on:

  • Implicit casting -> when/how does it happens
  • Are there rules as far as type inference go, in particular if you do something like: i := 2 + 3.2 You probably get a Float

Are there any rules/doc on this, or do I need to dig into the compiler code ?

Thanks.

tactics Tue 9 Mar 2010

Implicit casting -> when/how does it happens

You mean this?

Obj a := Int#
Type b = a
echo(b.slots)

As far as I understand it, it happens whenever you make an assignment or pass a variable and the incomming type (Obj in this example) is a superclass of the expected type (Type above).

You probably don't have access to the fcode, but a Coerce instruction is automatically inserted by the compiler.

# Are there rules as far as type inference go, in particular if you do something like: i := 2 + 3.2 You probably get a Float

3.2 is actually a Decimal, not a Float. And there is no automatic arithmetic coercion. To get this to compile, you must use 2.toDecimal + 3.2.

KevinKelley Tue 9 Mar 2010

i := 2 + 3.2

That's an error actually; no implicit conversions. That would have to be either 2f + 3.2f and i becomes a Float; or 3.2.round and i is (Int)5. I can't find the doc ref for that though.

These two pages are useful:

docLang::Expressions#typeChecking

docLang::TypeSystem#implicitCasts

tcolar Tue 9 Mar 2010

Kevin, thanks for the links (esp. docLang::TypeSystem#implicitCasts)

also it seem src/compiler/fan/steps/ResolveExpr.fan and src/compiler/fan/util/CallResolver.fan

Have most of the code dealing with that kind of stuff.

I guess my example was a bad one ... I meant that so far I had assume an inferred "expression" type can determined by it's "left-most" part ... but I'm just not sure whether it's always true.

KevinKelley Tue 9 Mar 2010

Pretty sure it's not "left-most", it's most-specific-that-fits, but I can't find what I'm remembering. I'd seen that somewhere in the ast stuff, though, I'm sure.

Edit: sorry, I think I was thinking of CType.common, which seems to be just for lists and maps. I think, for compound expressions, the shortcut conversion of a + b to a.call(b) applies, so then left-most is the effect.

brian Wed 10 Mar 2010

As others have said, there is no coercion b/w different types in Fantom (such as Int to Float). Although there is implicit casting. The key issue is that casting b/w things like Int and Int? is actually a coercion in Java (long vs Long).

However, the FFI does do coercion - for example int to Int (long).

Good place to look is compiler::CheckErrors.coerce and the associated boxing methods.

tcolar Wed 10 Mar 2010

I'm writing some more using tests and running them against all of fantom distro code (src and examples) .. anbd finding a few more issues, so will keep coming up with questions in the short future :)

Couple more questions:

  1. in fantom-1.0.51/examples/web/demo.fan at line 69, you got: new make(|This|? f) { f?.call(this) }

What is |This|? suppose to be? Is that a nullable function type, if so isn't a -> require nowadays, or is that something else.

  1. What is the rule when it comes to import duplicates, for example in some source file you have: using fwt \n using dom

Now both of those pods have an "Event" type, I currently show an error, but i guess it's legal, do you juts pick whichever comes first ?

Thanks.

tactics Wed 10 Mar 2010

What is |This|? suppose to be?

Yes, that's a nullable function type.

Usually, what you want is to give f a default value like this:

new make(|This|? f := null) 
{ 
  f?.call(this) 
}

(It looks like someone forgot the default when they wrote the demo class).

This way, if you don't provide a with block, you effectively have a default constructor.

What is the rule when it comes to import duplicates

When two pods import the same class, everything still compiles fine. However, if you want to actually reference/use the ambiguous class in your code, you must fully qualify it:

using dom
using fwt

const  class Bar
{ 
  static Void main()
  { }

  static Void onDomEvent(dom::Event e)
  {
    echo("foo")
  }

  static Void onFwtEvent(fwt::Event e)
  {
    echo("bar")
  }
}

Alternatively, you can specifically import one or another in a using statement, in which case it is clear which class you are referencing:

using dom
using fwt
using fwt::Event

const  class Bar
{ 
  static Void main()
  { }

  static Void onFwtEvent(Event e)
  {
    echo("bar")
  }
}

tcolar Thu 11 Mar 2010

Another question:

in: fantom-1.0.51/src/build/fan/tasks/JdkTask.fan

JdkTask inherits from Task

However Task is not defined in a using, or in the same folder as JdkTask (one folder up)

Now my code is failing to resolve this, and I know the reason is because i was considering a pod to be "one folder" which obviously is not the case.

Anyway, the real question is: What is the proper way to find which pod a source file is a part of (since it doesn't have a package declaration as in java).

I've struggled with that before and put my "folder name as pod name" hack in place, but I knew that was not gonna work for long :)

Now for this to be usable in any env like my IDE and unit tests, I need a rule to resolve the pod name which does NOT depend of the fan runtime (typeDB and company) ...

Would the proper way be something like:

Recurse up until finding "build.fan" and parse podName out of it (I'm worried that this parsing could be fragile too).

brian Thu 11 Mar 2010

The proper strategy is to walk up the directory tree until you find "build.fan" then use the information in that. The simplest thing to do is actually parse the build file, then construct it (but don't run it):

script := Env.cur.compileScript(`build.fan`.toFile).make
script.typeof.fields.each |f| 
{ 
  if (!f.isStatic)  echo(f.name + " = " + f.get(script))  
}

tcolar Thu 11 Mar 2010

I converted that to java and it seem to be working ok ... except for the "examples" The build.fan in there is a buildScript rather than a buildPod ... and so does not have podName defined.

What am I to do in that case ?

KevinKelley Thu 11 Mar 2010

That build task doesn't produce a pod; the fan files under there will obey the scripting rules.

Which most often means, a file is compiled by itself; may contain several classes; may use any pods in the system. Podname is autogenerated, munged from the directory, but nothing much sees it.

#934 adds using `uri` so scripts can refer to other scripts; and docLang::Env and #1014 discussing it is relevant too.

tcolar Thu 11 Mar 2010

Right, I'm not gonna index that or anything, but i'll do the same auto-generated podName thing for simplicity and consistency - at least for now.

brian Thu 11 Mar 2010

What you probably want to do is check if the build script subclasses from BuildPod.

The examples build script subclasses from BuildScript.

tcolar Fri 12 Mar 2010

If I compile this: Int[] list := [25, `jj`]

It does not complain ... I was wondering if that's legal .. it seem even runtime is fine with it.

brian Fri 12 Mar 2010

It will compile Obj[] is automatically cast to Int[]. However it will fail at runtime:

Int[] list := [25, `jj`]
echo(list[0])
echo(list[1])

If you remember we had a long discussion about how to deal with list casts. We don't actually walk the list to check each item, however we do generate the appropriate array type in Java which traps all sorts of things at runtime.

Also see #986 which will change behavior to untyped lists and maps to be typed based on LHS declaration.

tcolar Fri 12 Mar 2010

Right so in the IDE, it is the right thing to do to mark that as an error (if LHS types don't match declaration).

brian Fri 12 Mar 2010

I personally don't think the IDE should be any more strict on error checking than the Fantom compiler since that might come back to bite you (although extra lint like warnings certainly wouldn't hurt).

tcolar Mon 29 Mar 2010

I was doing profiling on my IDE and fixed all the hot spot, but I have one left I find odd.

I'm using Fantom runtime to check on the buildScript as we discussed earlier in this thread (as part of the pod resolution)

I want to check on what type of script it is (ie: buildpod vs buildscript)

Anyway I use this code

File buildFan = new File(folder, "build.fan");
fan.sys.File buildFile = new fan.sys.LocalFile(buildFan);
FanObj script = (FanObj) Env.cur().compileScript(buildFile).make();
String buildType = script.typeof().base().name();

This line: FanObj script = (FanObj) Env.cur().compileScript(buildFile).make(); takes over 10 seconds to execute (src/flux/flux/build.fan) !!

This seem very excessive for a small build file like that, any ideas ??

Note: I'll try to step through and see what exactly takes that long under the covers.

brian Mon 29 Mar 2010

Is it 10sec the first time, or 10sec everytime?

The first time that method is called the compiler code is getting loaded into memory.

On my machine loading the compiler isn't 10sec, but maybe 1sec (long enough to notice). What I do in fansh is run a test compile in the background as soon as fansh is launched. By the time you've typed an expression into the fansh and hit enter, typically everything is already loaded.

tcolar Mon 29 Mar 2010

You are right, it's just once ... but it takes way longer than 1sec here, around 9-10s. It's on my workstation too, which is a pretty quick machine (2 double core pentium D @3+MHz and 8GB ram).

I can live with it, since its juts once, but that just seem to be a long time.

brian Mon 29 Mar 2010

If it is 10sec, then I'd say something else is wrong. How long does it take to execute a simple hello world fan script on your machine? Should be about the same time (since compiler is loaded).

Login or Signup to reply.