OK, good. Let us know if you find any bugs or have any problems with it.
Printable View
OK, good. Let us know if you find any bugs or have any problems with it.
I've just updated the code quite dramatically. I'm not sure at this point if I've broken something - running pitman in game-mode seems to work correctly.
--
Mats
Alright. I'm tied up in a project at the moment, but as soon as I have a chance I'll check it out.
Looks good.
Paul
>> How's the port coming along?
I was actually waiting to see if you had found any bugs or missing features. If not, I suppose we can make a final release out of it, but I just wanted to make sure that everything was working properly before doing that.
Yes, it is working fine.
Paul
No, there have been some changes since the last release, so I'll try to post the newest one sometime near the end of the week (assigned with a 2.0 version number).
I've just pushed some more "splitting into separate files" changes.
The minibasic class itself was quite large, and I split out the compiler component into its own files - the compiler.cpp is nearly 700 lines in itself.
[I had to hack about a few other places as well, and I'm not quite happy about the "DEBUGGING" function, but I wanted something that works - I will hopefully fix it up better tomorrow].
And I've just uploaded a new version of the .exe.
--
Mats
Excellent. I'll check it out as soon as I have a chance.
Very nice work, matsp. It's a significant improvement.
Since it's dissertation hand in day at my old uni, I thought I'd relive last year and have a go at another parsing project. So this is my attempt, along with my old friends flex and bison I've put together an interpreter. It's not an all in one including the editor like yours is, but for giggles I made it into a com object, and to fully immerse myself in last year, buildable from the WDK/DDK build environments. It will execute mbi files from the command line if you change the build type to exe and uncomment the define at the bottom of main/main.cpp.
It seems to work with pitman and the one from Paul's site, but parenthesised expressions in a parenthesised function call like Window((X+9), (R-8)) for example might trip it up. It's an easy workaround so in best MS tradition, it's classified as won't fix.
Lastly, instead of the umpteen thousand word slog, I did something far more fun and chronicled this thread, inspired by medieval themes.
mini-basic - Google Code
matsp, I've noticed a couple of bugs in the latest version. For instance, if you run PITMAN and type 'R' to redo a level the redrawing of the options is off. Also, if you try to run REIGN a compile error is generated. Unfortunately, I'll be out of town for a couple of days, so I won't have time to take a closer look at the code until the end of the week.
Ok, I'll see what I can do...
--
Mats
Ok, so clearly out implementation is broken:
This is what it looks like in original minibasic:Code:Hello0
World
Hello0
World
Ready.
> list
10 FOR I = 1 TO 10
20 PRINT "Hello";
30 PRINT "World"
40 NEXT I
Ready.
>
Clearly, the ending semicolon is not honoured as "do not produce a newline".Code:Hello World
Hello World
Hello World
Hello World
Ready.
>list
10 FOR I = 0 TO 10
20 PRINT "Hello";
30 PRINT " World"
40 NEXT I
Ready.
>
I will go to be now, but I will try to debug it on the train tomorrow.
[By the way, I'm convinced there is no difference between the current and the previous implementation, so I'm pushing some minor code-cleanups - mainly in the clear-screen, avoiding using a vector to store the clear-character, and using FillConsoleXXX functions instead...]
--
Mats
I have identified the problem - but as I am at work at present, I can't submit the code (need to set up proxy settings - I may do that later on).
--
Mats
Fix (all four lines + a bit of "add braces around one-line conditionals) is now in place. New code is not compiled into an executable.
--
Mats
More messing about with the parsing. Using table to parse keywords, rather than using long if/else chain. Ended up using HORRIBLE syntax for class-member-function-pointers (I had most of the code ready by the time I got home, it took me until now to get the right syntax).
New executable for Paul (or anyone else) to try out as well.
Edit: Submitted another minor update that cleans up the new code a tiny bit.
And this is the line that took 5 hours to write:
Code:(((*this)).*((tab[i].emf)))();
--
Mats
O_o
This is why I hate working with other people!
They just don't listen!
Seriously though, you could have saved yourself some time if you'd read my notes. (Even if the relevant source is no longer used.)
Soma
link: matsp / minibasic / changeset / da336054aec1 — bitbucket.org
instructions: search the page for (ZSV)
Actually, that is the bit that I eventually copied to make this work - but not being used to member function pointers, I didn't quite get the bit about (*this) that you need to give the compiler what object to work on.
I know you said that it was difficult to get it to work, but I didn't realize quite what it was that was difficult, if you see what I mean. I was searching for solutions on the web, but of course the ones I found only use member function pointers from the class within itself.
I'm sorry I upset you...
--
Mats
O_o
I guess I should have added a "wink"... or something.
I do hate working with people who don't listen, but that was hyperbole.
"I didn't quite get the bit about (*this) that you need to give the compiler what object to work on."
You shouldn't have to use that syntax. This:
is correct. It just doesn't work on some compilers for whatever magic reasons. (The extra parentheses around the `->*' subexpression are mandated by the standard.) If you only care about GCC and MSVC (not sure about older versions) you can go ahead and change it.Code:(this->*tab[i].emf)();
"I found only use member function pointers from the class within itself."
I'm not sure what you mean here.
Soma
The quoted bit you don't understand was meant to say that when I searched for a member function pointer example (the C++ FAQ for example), I got something like:
(I just wrote that from memory, it may not be accurate)Code:class X
{
public:
void (X::*func)();
void f() { cout << "Hello, World" << endl; }
}
X x;
x.func = &f;
(x.*func)();
The key ingredient is that it's not calling a member function of X from within the instance x via a data structure OTHER than the class itself. Which requires the complex this-> or *this syntax. And that was the bit I didn't understand - and the error message from the compiler is completely and utterly meaningless [I also managed to get a "internal compiler error" by adding a line with () instead of braces in a struct initializer, which didn't exactly help the process of working...]
--
Mats
I just did a bit more refactoring of the code, splitting the interpreter code into it's own files.
--
Mats
I have fixed the "INPUT" command that I had broken. The fix is not yet submitted, as I fixed it on the way into work, and still haven't figured out how to make mercurial work through the proxy/firewall here at work.
As to why it was broken: when I refactored the compiler component, I changed the behaviour by reading the token. I fixed it by checking for "mode" earlier in the compiler, and not use the "compile_line" function (which also allows a whole lot of other things to be done that would be incorrect, in my view - I'm even tempted to change the behaviour to match the original minibasic implementation, rather than going through the complex compiler / interpreter chain - after all, the original only accepts numeric input).
--
Mats
"The fix is not yet submitted, as I [...] here at work."
If your company allows, mostly unrestricted, HTTP/HTTPS and you have a decent "always on" connection you can setup a private tunnel through your home computer.
You've done a good bit of shuffling. I'll take a look and see if I notice any outstanding bugs later if you want.
Soma
I do have "always on" when it comes to networking at home. But the work is on my work-laptop at the moment, which when I take it to work won't be able to access HTTP without a proxy. I'm sure there is a way to set mercurial to use a proxy, but I'd have to toggle it back and forth, aside from figuring out how it's done.
I'd appreciate a code-review/debug session, if you have the time. I'm going to push up the latest bits of code. I was trying to benchmark a bit vs. original minibasic. I'm surprised that we're not that much faster (but original minibasic is a bit hard to time - have to use stop-clock).
--
Mats
Doh! Running a program that does 1000000 loops in DEBUG mode is about the same speed as the original. With Release, it's about 3x faster in C++.
That's not because C++ in itself is faster, but because the C++ code works differently.
I'm going to post the executable with optimization. (g++ -O2).
--
Mats
"But the work is on my work-laptop at the moment, which when I take it to work won't be able to access HTTP without a proxy."
Oh, I did misunderstand what you were having trouble with...
(It's something like that.)Code:hg --config http_proxy.host=server,user=mats,passwd=password push
What I've done is setup a simple forwarding utility to cooperate with `hg' executable and a few associated scripts that setup environment variables that define how the forwarding utility operates.
In other words, at home:
at work:Code:> hg-loader
> hg pull blahblahblah
Then again... I don't like remembering things. Your mileage may vary.Code:> hg-loader --config work
> hg pull blahblahblah
Soma
O_o
Okay...
At some point, you completely broke the parser. I think I fixed a couple of bugs, but I found dozens more. It is, more or less, completely stuffed. (At the very least "REIGN.MBI" and "PITMAN.MBI" load and seem to run correctly. Of course, infinitely many others do not. For example, load "DFOREST1.MBI" and "DFOREST2.MBI".)
I'm not exactly sure why, but you two have reintroduced some old bugs. (For one, you dropped my console size implementation in favor of a different one which fails under various circumstances.) I fixed the problem related to the startup code, but it is poor bandage.
I fixed one of the oldest bugs related to handling a carriage return. See several posts back where I posted/linked a description and a snapshot of the problem in action.
I uploaded a new image built from MSVC. (It requires the MSVC2K3 runtime DLL.)
I accidentally loaded the old source which was naturally stripped of trailing blanks so the push has fake updates to the old source.
Soma
Huh? I have been testing with Pitman, which works just fine on my machine. But I did notice the window resize was a bit borked. But certainly not completely broken. I will have a look at your changes, tho'.
I found at least one bug: Your change for "get_symbol" to just return token broke this:
I think we really NEED to start looking at some automated testing (not because you broke it this time - I'm just as guilty of breaking stuff - one of my compiler changes broke INPUT for example). I'll look into making a module of the console that uses iostream so that we can produce output to a file, and compare it with an expected result. Probably not today tho.Code:5 A = 0
10 FOR I = 1 TO 10: A = A + I: NEXT : PRINT A
20 STOP
I'm going to completely delete the deprecated (or depreciated ;) ) files in my next submission. I don't think we want them any more - and if we do, we can always pull them back out of the archive.
--
Mats
"I have been testing with Pitman, which works just fine on my machine."
Oh no it doesn't. I did say "load and seem to run correctly". Look back through the thread to see the issue I'm talking about.
"I found at least one bug: Your change for "get_symbol" to just return token broke this:"
Not really... or not exclusively... it certainly didn't work as it should have. (I fixed several bugs with that and broke several others. I knew this, but my change seemed a net win.) I've updated the relevant bit to correct only the most obvious problem. (It is still broken in other areas. Or at least manifests from there...) I don't really understand why it exists. As for the other point of interest, I don't think it broke anything, but I've explicitly noted it with "(ZSV)" in the latest update so you know where/why I made the change.
"I'm going to completely delete the deprecated files in my next submission."
I did that just now. Actually, I did it several times. I kept getting a bad clone. (It seems to work now.) Still, several pointless commits. (One very pointless commit.)
Soma
This thread seems to have become a project discussion a long time ago. Does anyone think otherwise?
If nobody disagrees, I'll move it to the appropriate forum.
"This thread seems to have become a project discussion a long time ago."
Yep.
I was hoping that the host (BitBucket) would have a forum or something so we wouldn't have to use this thread at all.
"If nobody disagrees, I'll move it to the appropriate forum."
Are we talking the "Projects and Job Recruitment" board? That's probably a good idea.
O_o
Now that you've brought it up, I kind of wonder why laserlight didn't move it.
Soma
No problem to me.
I should have pushed my change to the get_symbol() - which is identical to your in all but the exact detail of how you check if it's a @.
I also started on a "consolestdiol.cpp" which can be used interchangably with "console.cpp" - it obviously doesn't do all the stuff that the full console does, but it would allow a set of automated tests for checking the language and functionality.
It will of course not check problems caused by console differences...
--
Mats
"I also started on a "consolestdiol.cpp" which can be used interchangably with "console.cpp" - it obviously doesn't do all the stuff that the full console does, but it would allow a set of automated tests for checking the language and functionality."
We just don't have the right primitives in place for that. Don't get me wrong, it would be better than nothing, but really, the "program" needs to communicate its state to the interpreter, and the interpreter needs to be able to communicate its state to... something at a higher level. And to be Bill, because of the inconsistency in the documentation and the original assembler I don't see how we can produce an automated testing tool. We need a real specification to work towards.
Soma
Well, I'm going to submit it anway - it's not going to hurt.
We probably should also cover cursor movement through metadata (or perhaps ANSI sequences?) so that we can "see" if something goes wrong with cursor positions.
And whilst we may not be able to test for some "standard", we should certainly be able to test for consistency in our own implementation - at least to the extent of spotting when something has gone wrong, rather than someone else discovering it several days later.
--
Mats
I agree with you completely. I'm just saying it would be nice if we had hooks in place to "watch" the compiler/interpreter and a real specification of the targeted dialect. That "something" is definitely better than our current "nothing".
Regarding the markup issue, instead of a simple ASCII input file and looking at the result after the fact--from a file dumped via your `stdioconsole'--why not markup everything? You--or we--could then add a few more lines--say in your `stdioconsole' or an object derived from it--to record a sample run of the program. I think this would be far superior to trying to craft the input files manually using ANSI escape sequences, but it does mean some extra work.
Soma
My plan was to capture the output from the test-code. Then when we change the code, it should match - or you have to explain why the new output is "better" (and reissue a new "template result").
Input would come from an input template. [Again, we probably can't simulate editing and such, but at least we will know if the input keyword is broken, and we can run multiple tests in a single "script" (batch file or so)].
It's pretty basic, probably because I believe in simple solutions, and I may not have thought it through very much.
--
Mats
To allow the console to be selected at runtime, I have un-singleton'd it.
--
Mats
I've submitted a few updates.
Some small bug-fixes, such as entering just a line-number should REMOVE that line, not end up with a line with just a number on it.
Loading a text file with an empty line in it would end the loading, whcih I don't think is what it should do.
There is a bug when loading text BASIC files by the way. Since the same scanner object is used for both reading the load command, and for inputting the text from the file, when we get back to the command loop, it fails the "scanner.empty()" check by an assert in the STL implementation (In VS 2008 Express Edition at least). I think it is because the string that is used for the input to scanner is "gone".
--
Mats
I'm pushing another refactoring, splitting out the functionality to run, list, load, save etc into separate functions. I will make it table-driven in a bit.
I have found a difference between the original minibasic and our implementation:
doing "list 1000" will list line 1000 only in our implementation. In the original code, it lists FROM line 1000.
--
Mats
I just took a few minutes to look through the changes and make a few notes.
1): Is the intent of `string awkward_save_helper()' to save with any file extension? If not, the `string awkward_save_helper()' implementation really should use something like `bool ends_with(string const&, string const&);' because most file systems and operating systems in use these days support internal '.' characters. If so, `void load(string name)' should probably drop the implied ".MBI" extension. (If both "test.123" and "text.123.mbi" is in the same directory "LOAD test.123" can't be interpreted as a potentially mistyped "text.123" with the implied extension.)
2): What is "direct.h" used for?! O_o
3): After finally realizing the problem I had with extending the virtual character codes, do you think it is time to make a couple of conversion tables so we can get the bigger bang for our precious bits?
4): Did you ever do anymore work with variable names?
Soma
How are things going?
Paul
Indeed, I was just doing some more work on that bit of code, and I was thinking exactly the same thing. And I think we should have different defaults for different types of files - e.g. TSAVE should save as test.bas, where SAVE saves as MBI - not quite sure what BSAVE should use.
Apparently not needed - I just commented it out and it still compiles just fine. I will remove it.Quote:
2): What is "direct.h" used for?! O_o
I'm quite a fan of table-driven programming, so yes.Quote:
3): After finally realizing the problem I had with extending the virtual character codes, do you think it is time to make a couple of conversion tables so we can get the bigger bang for our precious bits?
Not really - I decided to do some more refactoring first, and haven't got back onto it.Quote:
4): Did you ever do anymore work with variable names?
--
Mats
"not quite sure what BSAVE should use"
".MBB"?
"I'm quite a fan of table-driven programming, so yes."
O_o
"I decided to do some more refactoring first, and haven't got back onto it."
I was thinking around hooking that implementation to virtual character codes. (So that a non-windows implementation wouldn't have to stick with whatever extended keys may be used.)
Soma
Sounds good to me. I will work on that on the way home tonight. Should be doable if I don't get too sleepy on the train! ;) [The advantage of sitting on a train for about 90 minutes each day is that small projects like this gets done pretty neatly].
On the way here, I moved the long "if token == RUN ... else if token == LIST ... " into a table with function pointers.
Not quite sure how those things are related... Maybe what you were asking had nothing to do with what I was replying to - my reply was referring to making long variable names and allowing multiple arrays. Which makes little sense to your follow on point. So perhaps we can clear up what the original question was meant to be... ;)Quote:
"I decided to do some more refactoring first, and haven't got back onto it."
I was thinking around hooking that implementation to virtual character codes. (So that a non-windows implementation wouldn't have to stick with whatever extended keys may be used.)
Soma
--
Mats
"So perhaps we can clear up what the original question was meant to be..."
You answered the question I asked. ^_^
In other words, instead of checking against `17942' in the "MBI" source, you'd check against `CC_APPLE' without ever implying that this was syntactic magic for `17942'. (The same as, say, loading the value 13 into `@(2000)' by default and having users check against that instead of 13.)
Soma
I've submitted code to fix up the extension - at least in the basic cases. We probably need a better method, really. But at least we get appropriate names for the respective files.
Also submitted the table-driven parser of run, list, save etc.
And I FINALLY fixed the "load test.bas" causes an error!! I had to make a stack of the scanner, which isn't very good solution, but it's better than an error.
I also fixed the BSAVE command - it was broken if you just load a file and save again, because there was no compiled code. I just added a recompile.
I also shuffled a bit of code around, as per usual ;)
--
Mats
Paul,
Can you get the latest code from
matsp / minibasic / downloads — bitbucket.org
and give it a whirl. If something isn't working right, please let us know. If it IS working, please let us know that too.
If you know someone else who wishes to try it out as well, please feel free to point them towards the link above.
--
Mats
Would anyone object to putting #ifdef _DEBUG around debugging code? A quick hack in interpreter.cpp shows that it's saving about 8KB there alone.
--
Mats
I made another bit of code-clean-up - got about 30KB less code by re-arranging a bunch of things. [That's gcc-code, which I feel is quite bulky - mostly to handle exceptions].
--
Mats
Sounds good. Looks good, too.
Paul
I'm still a bit worried about the code size when using gcc to build the code. I was looking at uSTL, which may work better. Not sure if it supports "map" tho, or if it compiles for Windows! ;)
On the project front, I have now implemented the basics to allow long variable names - it allocates space for variables as needed, not based on "there are only 26 variables". It still doesn't actually allow long names as such, but it's just a question of adding a little bit of extra code.
--
Mats
And I've now submitted changes to support long variables. It appears to work fine - as usual, I used pitman as the test-code, and that loads and runs correctly.
Note that some variable names will need a keyword to make them work:
will produce an error when trying to run.Code:10 fork = 10
will work.Code:10 let fork = 10
20 print fork
I had to change a bit of the parsing code, and fix a the compiler::expect function to use match, because the old token_scanner::get_token() function was not able to provide tokens like "X11" or "A2B" - it stopped on any transition from alpha to non-alpha or vice versa. I think the new get_token is actually a bit easier to read and understand, as I could remove a couple of variables too - but it's got a lot more return statements now!
I think this is a good improvement. ]
Edit: I just pushed a new version number, as the format of the new files is different from old ones.
Unfortunately, the current code allows OLD pre-compiled files to be loaded and attempted to run, which falls over, so we probably need to change the version checking in some way that means that we can detect the "oldest version that works" - or perhaps simply say "the version number pre-compiled binaries will have to match" - any thoughts?
Next two things I will do:
1. Optional use of uSTL instead of STL - should produce radically smaller executable - not sure how much right now, but I'd be disappointed if it's more than half it's current size.
2. ANSI escape codes in consolestdio.
--
Mats
Looking good so far. Is a final release coming soon?
Paul
"Is a final release coming soon?"
This is software development. There is no final release; there is only the last release.
"Optional use of uSTL instead [...] half it's current size."
This is a spectacularly bad idea. The uSTL is not a replacement for the STL, copatble with the STL, or even an alias for the STL.
Instead you may as well use `std::vector<???>' for all storage requirements--replacing every other container--, pun `std::vector<??? *>' as `std::vector<void *>', and use the C IO routines directly. At least then you retain some level of portability and isolation, and the source will not give the allusion of being what it isn't.
Soma
Whilst it may be that uSTL isn't a plug-in replacement for STL, it is certainly capable of doing pretty close to everything that our current code is doing in STL - I'm still working through the code to make uSTL compile properly (I only just switched the computer on for the first time a little bit ago, so I haven't done ANYTHING on it today). I certainly think it's a better solution than the solution you are suggesting. Particularly since using uSTL means very few changes to the ACTUAL code, whilst your suggestion implies a lot of changes. And it's selectable, so anyone who wants to use STL can still do so [assuming recompile, of course].
I agree on the "final release". I think we are actually looking pretty good at the moment, and I would have no problem with the current version being used more widely.
--
Mats
"it is certainly capable of doing pretty close to everything that our current code is doing in STL"
NO. IT. IS. NOT.
A `std::vector<std::pair<x, y>>' masquerading as a `std::map<x, y>' is not even in the same universe as what we need.
"I certainly think it's a better solution than the solution you are suggesting."
O_o
That wasn't a suggestion; that was sarcasm.
*shrug*
If you want to waste time adding that as option solely because the resulting binary is a little large go for it.
Soma
Well, my wasted time has achieved this (I've just submitted the code itself, however, I will have to add all the USTL code to the local repo first - it's currently in a different directory).
Now for the results:
1. The compiled code is 183KB, instead of 549KB.
2. The speed of execution (simple benchmark) is approx 5x faster than Visual Studio [1], but about the same as gcc with stl.
[1] This is probably because of some feature we can switch off with a #define - I just haven't figured out what (because I've spent 0 time on that).
--
Mats
I'd say that's a significant improvement. :)
I have now posted the USTL code inside the Minibasic project.
Note that you still have to build the USTL by itself. I'm going to fix that...
--
Mats
OK, thanks.
And I've now ACTUALLY added the ustl code - it already had a .hg directory [becuase I used the hg git extension to pull in the code], and I had to recreate it by moving it to a fresh emtpy directory. All done now [I think - at least it said added 134 files, and the repo is about 200KB larger].
--
Mats
For some reason I get an error on -fvisibility-inlines-hidden and -Wno-overflow. I couldn't find the latter in my gcc docs (is it a new-ish option?), and the former, well maybe it just got left out of the mingw port of 3.4.2?
EDIT:
Oh, wait a minute. I don't see the switches in the makefile...what the heck? I'll post back as soon as I figure it out. :p
Oh, I see. It's in the USTL makefile. I guess I can just delete those entries from my copy of the makefile (possible side-effects?).
The flags are in config.mk - I haven't bothered to fix the warnings occurring in uSTL.
It may be easies to remove flags that cause the offending code.
--
Mats
Fixed those, but now I get compilation errors from utuple.h on lines #293 & #294 (ambiguous template specialization).
Ah, I just realized I've been using gcc 4.3.0 for a long time! I thought I was using my trusted 3.4.x version!
When I switch to the 3.4 version I get tuple errors too. I'm pretty useless with templates, so I'm not sure how to fix that... :(
--
Mats
Really, 4.3.0? Man, I guess it's time for an update. :p
I'm on "4.5-Something". ^_^
Mats: I got the email regarding the issue (#2). I've suggested before that you close them both.
Sebastian: Try changing the expansion--the crappy implementation macro--to specify the target template type of the nested template for every use. (The example explains the suggested modifications.)
Soma
Code:template <> template <> \
inline const tuple<2,type>& tuple<2,type>::operator = (const tuple<2,otype>& v)
Code:template <> template <> \
inline const tuple<2,type>& tuple<2,type>::operator= <otype> (const tuple<2,otype>& v)
No go. And unfortunately, mingw's latest stable version of g++ is 3.4.5, and it won't compile it, either. I'll see if I can figure out a workaround.
Well, it finally compiled after commenting out these lines:
...although I have no idea if that might introduce any bugs. Also added a copy of Borlands touch.exe, since mingw doesn't come with one. And of course the configure and makefile changes.Code://UINT32_TUPLE_SPECS(int16_t,uint16_t)
//UINT32_TUPLE_SPECS(uint16_t,int16_t)
I'd recommend getting the GnuWin32 package, instead of using a 16-bit touch.
But we could probably remove the need for reference to ranlib altogether.
--
Mats
I have fixed several minor issues
- Saving to text-file (.BAS) (caused by calling fstream::write() with a zero length which set the "fail" bit).
- Compile errors with older compiler.
- Makefile problems.
I still have a problem LOADING .BAS files.
--
Mats
I have made a bit of progress with the loading .BAS files, but not quite there yet. Now it simply needs to stop when the file is consumed...
--
Mats
I just pushed another bit of updates.
However, I think I've broken the "save" functionality, so I need to unbreak that. Nothing obvious shows up in the past updates, so I'm not sure what it is. Still works to save with "tsave".
--
Mats
I just started on "string variables". It's nowhere near complete.
Also done a whole lot more code cleanup.
--
Mats