00:00:55 | * | KarmaScript quit (Ping timeout: 250 seconds) |
00:25:27 | * | zahary quit (Read error: Connection reset by peer) |
00:56:05 | * | Trixar_za is now known as Trix[a]r_za |
06:51:04 | * | Trix[a]r_za is now known as Trixar_za |
07:25:26 | * | Trixar_za is now known as Trix[a]r_za |
09:18:17 | * | zahary joined #nimrod |
09:21:27 | * | XAMPP quit (Read error: Connection reset by peer) |
12:38:36 | Araq | ping zahary |
13:51:55 | zahary | hi Araq |
14:42:37 | * | SchalaZeal joined #nimrod |
14:42:59 | SchalaZeal | Well it looks like I flubbed a little on that PCRE wrapper |
14:56:37 | SchalaZeal | hmm... the SSL functionality of sockets module... should I be able to call s.wrapSocket(protTLSv1) if s is a TSocket? |
14:58:07 | SchalaZeal | oh wait |
14:58:13 | SchalaZeal | need to define ssl |
14:59:03 | SchalaZeal | Is there a way I can programmatically define symbols? Passing them over command line every compilation is getting annoying |
15:03:12 | Tasser | SchalaZeal, symbols? |
15:05:38 | SchalaZeal | like... for example, sockets module requires "ssl" to be defined |
15:06:16 | SchalaZeal | Why can't I just put something like define(ssl) instead of having to pass -d:ssl every compile? |
15:06:40 | SchalaZeal | err SSL features of the module require it I mean |
15:08:18 | SchalaZeal | something like C's define |
15:08:23 | SchalaZeal | err #define |
15:08:42 | Tasser | you could use a projectfile |
15:08:57 | SchalaZeal | how? |
15:10:38 | Tasser | ehm |
15:11:26 | * | XAMPP joined #nimrod |
15:14:26 | SchalaZeal | Well a bit of good news... just made a client for the Wired P2P protocol |
15:16:21 | SchalaZeal | all it does is fetch and echo server info currently |
15:16:52 | Tasser | c2nim? :-) |
15:17:32 | SchalaZeal | doesn't c2nim just transform #define ssl into const ssl = true? |
15:18:24 | SchalaZeal | I don't think that counts as a define, does it? |
15:22:59 | SchalaZeal | ...oh nuts....... |
15:23:40 | SchalaZeal | Apparently I now have to find some way of getting Nimrod to read an image file into a Base64 encoded strong |
15:23:42 | SchalaZeal | string* |
15:24:15 | SchalaZeal | lucky me... |
15:24:44 | Tasser | SchalaZeal, readfile + base64 encoding? |
15:27:56 | SchalaZeal | you mean like store an entire PNG file as a string? |
15:30:54 | SchalaZeal | .......in terms of saving settings, that's gonna be a big .cfg file lol |
15:37:45 | Tasser | SchalaZeal, what's the difference between a png file and a string? |
15:38:05 | SchalaZeal | ..........I don't know...heh |
15:38:59 | SchalaZeal | in terms of sending a Base64 encoded image over a socket though, I'm a bit worried if the wrong line length will bork the image |
15:42:58 | SchalaZeal | or is that for readability? |
15:53:40 | SchalaZeal | ah nvmd... I'll test it |
15:54:44 | * | SchalaZeal quit (Quit: Konversation terminated!) |
16:15:53 | Araq | ping zahary (again) |
16:16:01 | zahary | pong |
16:17:49 | Araq | ah finally |
16:22:01 | Araq | so any opinion on the new integer promotion rules? |
16:33:42 | dom96 | hello |
16:34:04 | dom96 | ok, so I am off work tomorrow. |
16:34:10 | dom96 | So, Nimrod time! |
16:34:20 | Araq | hi dom96, cool |
16:34:27 | Araq | my connection is getting worse again |
16:34:44 | dom96 | Well as long as you have a BNC you should be fine. |
16:35:14 | Araq | no ... it's *very* annoying |
16:35:41 | dom96 | Call your ISP and complain? |
16:36:04 | Araq | well they warned me ... |
16:36:15 | Araq | to use a new router for DSL "3000" or whatever ... |
16:37:26 | dom96 | lol? |
16:37:55 | dom96 | Get a new router then :P |
16:40:15 | dom96 | hrm, so I guess I misspelled the deprecated pragma in ssl.nim? |
16:45:05 | Araq | yeah ... |
16:45:10 | Araq | and the tester still doesn't test that at least every module compiles :-/ |
16:48:51 | dom96 | Araq: Yay. Your changes didn't break jester :) |
16:49:12 | Araq | webapps don't do much integer math I suppose |
16:49:22 | dom96 | yep :P |
17:08:18 | * | fowl quit (Ping timeout: 246 seconds) |
17:11:03 | zahary | Araq, I haven't tried to write code agains the new rules yet - do you have some doubts about them now? |
17:17:05 | Araq | zahary: I have 2 doubts: |
17:17:42 | Araq | 1) compiler now disallows implicit conversion from int to int32 |
17:17:50 | Araq | which can be annoying for C interfacing |
17:18:14 | Araq | 2) var x = 90 mod 2 |
17:18:26 | Araq | makes 'x' of type 'range[0..1]' |
17:18:43 | Araq | with the resulting range check errors should 'x' be re-assigned |
17:18:57 | Araq | maybe you wanted 'x' to have type 'int' instead ... |
17:20:07 | Araq | but it's the proper behaviour for 'let' |
17:20:28 | Araq | and I don't want to have different type inference rules for 'var' and 'let' |
17:21:32 | Araq | however I don't think (2) is an issue; it's quite common that the constraint for the 'var' should also hold for re-assignments |
17:38:38 | zahary | hmm, range[0..1] sounds useful in theory if it allows for detection of errors in array indexing and ommiting range checks in sequence checks |
17:40:01 | zahary | I guess, there are two types of range types tho - open ranges that can "grow" by subsequence oprations like assignment and closed ranges that follow the current behavior |
17:41:52 | zahary | I originally have imagined such types as dependent type properties and with dependent types it's perfectly fine to know something at some point and then to revert to "unknown" state |
17:42:16 | Araq | yes it's the beginning of dependent typing |
17:42:28 | Araq | it quite useful to be able to do: |
17:42:41 | Araq | proc random(n: int): range[0.. n-1] |
17:42:52 | Araq | but that's of course not supported |
17:42:59 | Araq | instead you have to make 'random' a template |
17:43:06 | zahary | did you understood my point - I think I used too many words :) |
17:45:19 | Araq | I'm not sure I got your point |
17:45:55 | Araq | but I knew we both want dependent typing :-) |
17:47:03 | zahary | when a variable is typed range[0..1], this just means that the compiler have proven that at this point in the program these are the possible values. I think it's perfectly fine the type to be relaxed later to range[unknown] if the variable is reassigned (the compiler no longer knows anything about it) |
17:47:32 | Araq | I see |
17:47:44 | Araq | that's not good |
17:47:55 | Araq | var x = 4 mod 2 |
17:48:06 | Araq | p(x) # pass to uint8 implicitely |
17:48:14 | Araq | x = 400 # argh |
17:48:39 | Araq | 'range' affects type compatibility |
17:48:54 | Araq | you can't pass an 'int' to an uint8 implicitely |
17:49:22 | Araq | and I don't want to re-typecheck |
17:50:14 | Araq | I'd rather make the compiler promote an implicit range back to 'int' for variables |
17:50:25 | Araq | and only keep the range for 'let' |
17:50:31 | Araq | (and 'const' of course...) |
17:50:35 | zahary | well, the way I imagined it previously, the range part is ortogonal to the real machine type |
17:51:01 | Araq | sure but it's just too cool to allow: |
17:51:08 | Araq | passToByte(x and 0xff) |
17:51:25 | Araq | so I conflated these notions |
17:53:26 | Araq | well to be honest |
17:53:51 | Araq | I'd simply disallow any kind of implicit conversion between integer types |
17:54:07 | Araq | as it only leads to sloppy coding |
17:54:14 | Araq | but people don't accept that I hink |
17:56:46 | zahary | if range[0..1] is the begging of dependent typing, try to imagine how you'd like ints to work when dependent typing is fully supported |
17:57:39 | Araq | well for a start we'd allow variables as bounds range[n..m] |
17:57:46 | zahary | shouldn't my "range relaxing" scenario work then? |
17:59:22 | Araq | maybe :-) |
17:59:46 | Araq | it's easy to oversee something |
18:00:05 | Araq | as we already have some fixpoint computations planned |
18:00:27 | zahary | maybe you should treat the range as appendix to the int type |
18:00:27 | zahary | x and 0xff produces (int, range[0..ff]) which is implicitly convertible to byte |
18:00:53 | Araq | well that's how it is implemented already |
18:01:02 | Araq | unless I misunderstand you? |
18:01:16 | Araq | a 'range' has a base type |
18:01:23 | Araq | range[0..1] of type int |
18:01:25 | zahary | well, then x = 400 just reassigns the type to (int, range[400..400]) |
18:01:57 | Araq | but it's too late then |
18:02:15 | Araq | the compiler already let 'x' be passed to the proc |
18:02:32 | zahary | maybe we're imagining how dependent typing should work in different ways |
18:03:08 | Araq | well apparently you also want flow analysis here |
18:06:42 | zahary | yes, in my mind the dependent properties are updated by the statements in the program - each op/proc call has a runtime part and compile time part indicating how the dependent type variables of the participating symbols are to be updated |
18:08:07 | Araq | well that's what an optimizer does |
18:08:35 | Araq | they always tend to have lots of cool stuff that you really want in the frontend in the first place ... |
18:09:04 | Araq | "optimizing" and "proving properties of the program" is really the same |
18:09:16 | Araq | it doesn't make too much sense to only have it in a backend |
18:09:27 | Araq | and then lose all the nice warnings etc. |
18:10:30 | zahary | well, here again range checks are the most common type of analysis and maybe some optimizers already care about them, but I imagine all sort of domain specific analysis that can be performed by libraries - the obvious ones are things like "you should not call function X, before you have done Y" |
18:10:57 | Araq | sounds like type state to me :-) |
18:11:20 | zahary | well, yes - if you remember that's how I originally was calling the whole feature :) |
18:11:36 | Araq | you? |
18:11:46 | Araq | I thought it was "Rust" ;-) |
18:11:55 | Araq | well the authors of the paper really |
18:12:00 | Araq | (forgot the names) |
18:14:00 | Araq | anyway: I plan to have a new field in an AST node |
18:14:09 | Araq | that's "constraint: PNode" |
18:14:16 | zahary | I meant I was advertising the rust type state system to you (which is less general btw) and a bit later we settled on calling it dependent typing |
18:14:43 | Araq | for all sorts of analyses |
18:15:11 | Araq | but I'd prefer to get rid of the "comment" field so memory usage stays the same :-) |
18:15:20 | Araq | before that |
18:15:24 | zahary | I think these are just variables attached to symbols - I also used to call them symbol-attached properties |
18:15:46 | zahary | every type defines what kind of compile-time variables it has |
18:16:05 | Araq | ah yeah I thought about this too: |
18:16:24 | Araq | if x >= 0 and x <= 10: |
18:16:38 | Araq | # x's constraint in this branch is range[0..10] |
18:16:45 | zahary | yes, exactly |
18:17:04 | Araq | just a mapping from PSym to some constraint in PContext, right? |
18:17:15 | Araq | in fact, a shadow symbol table ... |
18:18:31 | Araq | btw do you have any experience with coq? |
18:18:43 | zahary | why do you say "constraint in PContext"? I think here x is of type int and it has maxValue and minValue compile time properties, so the symbol x just holds a bag of properties |
18:19:18 | Araq | but you need to push and pop properties |
18:19:30 | zahary | it's up to the int library to define how maxValue and minValue will be used - well, obviously here the "int library" is the compiler itselft |
18:19:46 | Araq | and these stack operations can be attached to the scoping rules |
18:20:07 | Araq | that's what I mean by "shadow symbol table" |
18:20:30 | Araq | the constraint only holds over some statements |
18:20:39 | Araq | it's not only a property of 'x' |
18:22:41 | zahary | alright, I see what you mean, but I see this as implementation detail - I actually imagined a implementation where the updates (and restores to previous values) of these properties are intermingled with the regular code that opens and closes scopes |
18:35:48 | Araq | I thought we're already talking about the implementation :-) |
18:36:07 | Araq | but I have to go soon, some final remarks: |
18:36:37 | Araq | 1) we really need a better notion of construction to get rid of 'nil', for instance |
18:37:14 | Araq | 2) control flow analysis should be simpler than in other languages as we have no 'goto' |
18:38:03 | zahary | well, we need default contructors - strings are sequence are quite a trap to newcomers |
18:38:27 | Araq | yeah I know |
18:38:43 | Araq | but maybe a warning for a start suffices |
18:38:53 | Araq | (which requires control flow analysis) |
18:39:01 | zahary | I also want to get some of the C++ partial construction unrolling behavior |
18:40:03 | Araq | ugh I don't ... |
18:40:13 | Araq | too much magic for my taste ... |
18:40:28 | zahary | result.foo = bar() |
18:40:28 | zahary | code code |
18:40:28 | zahary | should be transformed |
18:40:28 | zahary | result.foo = bar() |
18:40:28 | zahary | try: |
18:40:28 | zahary | code code |
18:40:28 | zahary | except: |
18:40:29 | zahary | destroy result.foo |
18:40:29 | zahary | raise |
18:40:44 | zahary | but it's hard to write correct code without these |
18:41:40 | Araq | maybe ... |
18:41:44 | zahary | here is the classic C++ paper that took several years to disprove in C++ |
18:41:45 | zahary | http://ptgmedia.pearsoncmg.com/images/020163371x/supplements/Exception_Handling_Article.html |
18:42:13 | zahary | and the only solutions heavily rely on partial unrolling behavior |
18:47:06 | dom96 | s/is/are/ |
18:47:07 | dom96 | DAMN IT |
18:51:13 | Araq | I dunno |
18:51:15 | Tasser | dom96, git push --force |
18:51:37 | Araq | these examples are always about transactions in a way |
18:51:59 | Araq | "the object is still in a valid state should 'push' throw an exception" |
18:52:28 | Araq | transactions are hard and also expensive |
18:52:40 | Araq | or better: *and thus |
18:53:27 | Araq | I haven't made up my mind yet what's the best approach to solve the transaction problem |
18:53:54 | Araq | but the constructor becomes too special if its does partical unrolling behind your back IMO |
18:54:56 | Araq | this feature may push people to use heavy constructors that do much work |
18:55:04 | Araq | to get this nice partial unrolling feature |
18:58:06 | zahary | every function is a contructor - that was a valid point of yours |
18:58:46 | Araq | yeah |
18:58:50 | dom96 | Tasser: hrm? |
19:00:07 | Araq | I'm not against unrolling, but we should think if we can't detach that from constructors |
19:00:07 | zahary | so, it's just a rule about how the constructed bits of the result variable are automatically destroyed if the proc is left with an exception |
19:00:26 | Araq | it might turn out to be: |
19:00:27 | Tasser | dom96, change history, push :-) |
19:00:40 | Araq | proc x(obj: T) {.unroll.} = ... |
19:00:46 | dom96 | Tasser: meh, it's not that important :P |
19:00:50 | Araq | instead of 'destroy' ... |
19:00:52 | Tasser | Araq, welcome to reversable programming? |
19:01:31 | zahary | er, destroy is for destructors only, and we care about construction here |
19:01:43 | Araq | make that: |
19:01:50 | Araq | 'in addition to destroy' |
19:01:58 | zahary | but if you dislike unrolling by default, pragma is good enough for me |
19:06:14 | Araq | zahary: yeah sure |
19:06:55 | Araq | however what about 'except: unroll(x)'? |
19:10:31 | zahary | it's bassically what the proposed solution is doing automatically |
19:11:06 | Araq | not really |
19:11:13 | Araq | if I understood it correctly |
19:11:16 | zahary | for every destructable variable that is field of the result, insert the except exit code |
19:11:25 | Araq | C++ writes the 'unroll' code for you |
19:12:06 | zahary | yes, if you used the initializer list properly |
19:12:29 | Araq | I suppose you then write 'removeFile(f)' in the destructor? |
19:12:43 | Araq | I'm thinking about a proper transaction |
19:12:45 | zahary | what is removeFile here? |
19:12:49 | Araq | which involves file creation |
19:13:09 | zahary | say you want to create 2 files in a constructor |
19:14:15 | zahary | you'll have to implement a separate File class and have 2 members of it |
19:14:15 | zahary | you have to initilize them like this: CompoundCreation(f1, f2: path): file1(f1), file2(f2) {} |
19:14:37 | zahary | then C++ will erase file1 if something happened during file2 initialization |
19:16:53 | Araq | alright |
19:16:57 | Araq | that's what I thought |
19:23:15 | Araq | it's also not correct IMHO ;-) |
19:23:36 | Araq | as a destructor should close the file on success, not erase it |
19:23:45 | Araq | 'unroll' is not 'destroy' |
19:24:01 | Araq | it's an "in case of error, do this" |
19:24:35 | Araq | in C++ you likely have a bool and then a distinction in the destructor |
19:24:53 | Araq | as it needs to do different things |
19:35:00 | zahary | well, what's the goal of the class? to temporarily create 2 files that should be deleted on program exit? then the members type will be something like TempFile |
19:35:19 | zahary | or is the goal to either create 2 file or none |
19:35:36 | Araq | actually the goal is to write immediately in a transaction log |
19:35:54 | Araq | and to delete these on success |
19:36:07 | Araq | (or something like that) |
19:36:45 | Araq | maybe you wouldn't put the "success" operation in a destructor though |
19:38:23 | Araq | however, my point is: |
19:38:38 | Araq | construction fails -> C++'s invokes a destructor |
19:38:50 | Araq | but in the destructor the information is lost |
19:39:01 | Araq | that it's run because of an *exception* |
19:39:10 | Araq | so you need to reconstruct this information somehow |
19:39:14 | Araq | in the destructor |
19:39:48 | Araq | information is thrown away |
19:39:52 | Araq | not good |
19:40:36 | zahary | I don't follow - if there was exception during construction, the destructor *wont* be called - the point is that it's not safe to call it, because the object may be in incomplete and arbitrary state |
19:41:01 | zahary | but if you have constructed parts of the objects, you have to destroy them somehow, otherwise there may be leaked resources |
19:41:31 | Araq | in your example, file1's destructor is invoked |
19:41:43 | Araq | if CompoundCreation fails |
19:45:59 | zahary | yes - and I asked what's the goal of the CompoundCreation? if it's not an object holding 2 temporary files, but rather have to represent a transaction of "either create 2 files or none", then you have to use something like FileCreationGuard that has a "discharge" method or you can use regular try/catch blocks |
19:49:58 | zahary | if your point is that there can be another class of destructors that only trigger during exceptions that's interesting idea |
19:50:37 | zahary | it's easily handled with a template tho |
19:51:06 | zahary | template createFileInTransaction = |
19:51:11 | zahary | createFile |
19:51:20 | zahary | except: deleteFile |
19:52:05 | Araq | "if your point is that there can be another class of destructors that only trigger during exceptions that's interesting idea" |
19:52:06 | Araq | that is my point, yes |
19:56:31 | Araq | oh and I know that C++'s way is sufficient btw |
19:56:41 | Araq | I'm trying to find an IMHO cleaner solution though |
19:57:03 | zahary | alright, but for leak prevention the partial unrolling is very helpful |
19:57:20 | * | fowl joined #nimrod |
19:58:19 | Araq | I'd think so as these are C++'s major pain point :-) |
20:10:01 | fowl | is there an example use of the json parser |
20:11:57 | fowl | how come TJsonNode/JFloat/fnum isnt exported, it is the only value in TJsonNode that isnt |
20:12:46 | Araq | it is exported in the github version |
20:12:48 | dom96 | They are exported. |
20:12:56 | Araq | was just a typo in the old version I think |
20:13:01 | fowl | ah |
20:13:26 | Araq | marshal.nim uses the json parser |
20:19:29 | fowl | wtf i added the * locally but i still get Error: unhandled exception: fnum is not accessible [EInvalidField] |
20:22:33 | Araq | lol |
20:22:56 | Araq | if it's runtime error, it's no visibility issue |
20:23:11 | Araq | you try to access the field even though it doesn't exist |
20:23:33 | fowl | oh |
20:23:45 | fowl | so its probably not a float but an int |
20:24:02 | Araq | n.kind will tell you |
20:26:27 | dom96 | Araq: https://github.com/nimrod-code/nimbuild/issues/3 Thoughts? |
20:27:55 | Araq | sounds good but there are more important things to do |
20:28:10 | dom96 | I know. |
20:28:15 | dom96 | Just trying to organise my ideas :) |
20:29:05 | Araq | and I would not use jester for it, frankly |
20:29:17 | Araq | it's not really a webapp |
20:29:25 | Araq | plus jester has to change quite a bit |
20:29:35 | Araq | once we have a better templating system in place |
20:29:47 | Araq | I mean a better macro system |
20:30:13 | dom96 | It's a website. |
20:30:18 | dom96 | It can use jester :P |
20:31:16 | Araq | what? |
20:31:37 | Araq | it's mostly a backend system with a static "results" page, right? |
20:32:07 | dom96 | Read my ideas again |
20:33:10 | Araq | I see |
20:33:20 | Araq | you're about to make it a real webapp |
20:33:43 | Araq | I hope you keep security in mind then ;-) |
20:34:45 | dom96 | No, I want to let everyone rebootstrap at will :P |
20:37:35 | Araq | good :D |
21:13:09 | Araq | ah there is another idea I had, zahary |
21:13:32 | Araq | we have extended string literals right now |
21:13:49 | Araq | and we now have 0i32 (without the ') |
21:14:13 | Araq | I want to support 0ms |
21:14:26 | Araq | that would be transformed to: |
21:14:37 | Araq | `? ms`(0) |
21:14:41 | Araq | by the compiler |
21:15:23 | Araq | so you don't need to have a proc named 'ms' (which is short enough to collide with something) |
21:16:33 | Araq | but then for consistency the raw string identifiers should be transformed to: |
21:16:47 | Araq | `re ?`("[a-z]") |
21:23:25 | zahary | I had similar idea for my hypolang - there were supposed to be namespaces there and 2ms is mapped to units.ms(2) |
21:23:25 | zahary | for a nimrod-based solution I considered mapping it to unit(static{"ms"}, 2) |
21:23:45 | zahary | ... with another helper that creates such overloads more easily |
21:25:30 | zahary | `? ms` looks nice too |
21:27:41 | Araq | alright |
21:27:48 | Araq | still unsure to break code again |
21:27:48 | zahary | I spent quite some time thinking whether 12ms2 (as in miliseconds squared) should be mapped to ms(12, 2) or to ms2(12) |
21:28:09 | Araq | map it to ms2 |
21:28:38 | zahary | there are some possible generalizations when playing with the powers |
21:28:47 | Araq | I know |
21:28:53 | Araq | but I think it's overkill |
21:29:54 | Araq | and you really want ms² anyway |
21:30:09 | Araq | and nimrod's unicode support should allow for that |
21:31:29 | zahary | well, the IDE is supposed to provide the pretty printing |
21:31:57 | Araq | afaik the clay guys had 0i16 and now have 0s (for 'short' I think) |
21:32:06 | Araq | as it's ugly to look at |
21:32:42 | Araq | I like 0i16 better though |
21:32:49 | Araq | as 'short' is arbitrary |
21:32:58 | zahary | and s even more |
21:33:01 | Araq | and 0s means "0 seconds" anyway |
21:33:08 | Araq | ;-) |
21:33:15 | zahary | yep :) |
21:36:33 | Araq | but the string literals are problematic |
21:36:48 | Araq | re("abc") # common too |
21:37:05 | Araq | and we also have: |
21:37:16 | Araq | sql"select ..." |
21:37:25 | Araq | sql(dynamicQuery) |
21:37:33 | Araq | and TaintedString"" |
21:37:57 | zahary | yeah, I don't think you should change the prefix form |
21:38:53 | Araq | yeah the consistency is not worth it |
21:39:04 | Araq | units are different from extended string literals anyway |
21:39:17 | Araq | (a bit) |
21:42:13 | zahary | I once considered this too |
21:42:13 | zahary | proc milliseconds(x: int): TTime[..., 1000] {.shorthand: 'ms'.}= |
21:53:17 | Araq | hm as names 're' and 'sql' are already "optimized" for string literal usage |
21:53:19 | Araq | so maybe "shorthand" is the way to go |
21:53:22 | Araq | but I need to sleep now |
21:53:28 | Araq | good night |
22:16:06 | * | XAMPP[0] joined #nimrod |
22:16:41 | * | XAMPP_ joined #nimrod |
22:17:22 | * | Trix[a]r_za is now known as Trixar_za |
22:20:30 | * | XAMPP quit (Ping timeout: 264 seconds) |
22:20:37 | * | XAMPP[0] quit (Ping timeout: 246 seconds) |
22:26:06 | * | XAMPP_ quit (Quit: There is no such thing as coincidence, only the inevitable.) |
22:42:06 | * | XAMPP joined #nimrod |