August 01, 2007
Jarrett Billingsley wrote:
> "Robert Fraser" <fraserofthenight@gmail.coim> wrote in message news:f8pdsq$1p4l$1@digitalmars.com...
>> Awesome; great job. From a cursory look, I like what's going on there, but the things keeping me from turning away from Perl for general scripting purposes are the lack of an eval() and first-class regexes. Still, this looks like an awesome language for scripting within the context of another application, which I guess is the point.
>>
>> Anyways, congratulations on 1.0!
> 
> Oh, but it *does* have eval!  OK, so because of the design of the implementation, it can't access local variables, but it's still there in both the scripting language and the native API.
> 
> Thanks for the reply :) 
> 
> 

Ah, but since it *does* have access to 'varargs', could you not use loadString (eval's older brother of sorts) and pass the generated function the locals you need?  :)

 ____[ MiniD ]____________________________________
|
|  local x, y ;
|  // bunch of stuff
|
|  x, y = loadString("
|    local x, y = varargs ;
|    // do some dynamic stuff with x and y
|    return x, y;
|  ")(x,y);
|_________________________________________________

Sure its more explicit... it also means the code string doesn't have to actually know the names of the local variables; ie, you could reuse the same string for other functions, or better yet store the generated function under its own name and use it like any other function.

-- Chris Nicholson-Sauls
August 02, 2007
"Chris Nicholson-Sauls" <ibisbasenji@gmail.com> wrote in message news:f8qrm5$131h$1@digitalmars.com...
>  ____[ MiniD ]____________________________________
> |
> |  local x, y ;
> |  // bunch of stuff
> |
> |  x, y = loadString("
> |    local x, y = varargs ;
> |    // do some dynamic stuff with x and y
> |    return x, y;
> |  ")(x,y);
> |_________________________________________________

Pretty ingenious idea :D


August 02, 2007
Very nice. If you wouldn't mind (and if you have the time), I'd love to read more about the design of the MiniD interpreter. Are you using a parser generator for the lexing/parsing? Once you have your AST, do you internally generate VM bytecode, or do you walk the AST? What kinds of optimizations do you do? Is the VM/Interpreter purely stack-based, or do you use simulated registers? How do you implement your dynamic typing?

Have you read any of the documentation of the parrot project? Back when they were publishing more regular updates, I enjoyed reading about their implementation details. If you're interesting in writing about your experiences designing and implementing MiniD, I'd be very interested in reading about them.

Thanks!

--benji
August 02, 2007
Jarrett Billingsley wrote:
> After more than a year of brainstorming, writing, re-writing, taking ideas from other languages and generally having a good time, I've come to what I feel is a good place to call it 1.0.

Congratulations!

I have yet to test MiniD, but from what I've seen so far it really kicks ass. <3

Keep up the good work!

Best regards,
Alex
August 02, 2007
"Benji Smith" <dlanguage@benjismith.net> wrote in message news:f8rmam$2a74$1@digitalmars.com...
> Very nice. If you wouldn't mind (and if you have the time), I'd love to read more about the design of the MiniD interpreter. Are you using a parser generator for the lexing/parsing? Once you have your AST, do you internally generate VM bytecode, or do you walk the AST? What kinds of optimizations do you do? Is the VM/Interpreter purely stack-based, or do you use simulated registers? How do you implement your dynamic typing?
>
> Have you read any of the documentation of the parrot project? Back when they were publishing more regular updates, I enjoyed reading about their implementation details. If you're interesting in writing about your experiences designing and implementing MiniD, I'd be very interested in reading about them.

That's a good idea for a wiki page.  I'll put more stuff in the "Compilation" and "Execution" sections of the spec.

But in short: I wrote my own lexer/parser from scratch, based on the DMD frontend.  It's a recursive descent parser.  Once the AST has been generated, the compiler walks the tree and generates bytecode.  The VM is a pseudo-register design.  Rather, it's more like a native CPU stack, where enough stack space is allocated for a function's call frame, and then all accesses to the stack are by offsets from the function's base pointer.  (If you're at all familiar with the internal workings of Lua, MiniD works the same way.)  The compiler, though, does use a sort of stack machine to convert expression ASTs into the RISC-style three-op bytecodes: it pushes expressions onto the expression stack, and an operation (like add, call, etc.) corresponds to a pop, which actually writes the code.  This is my own design (though I wouldn't be surprised if there were other compilers that used a similar method) and it works pretty well :D

Just as an example, here's some MiniD code:

local x = [1, 2, 3, 4, 5];

for(local i = 0; i < #x; i++)
    writefln("x[", i, "] = ", x[i]);

And here's something like what it compiles to (did this in my head so it might not be exact):

Constants:
0: 1
1: 2
2: 3
3: 4
4: 5
5: 0
6: "x["
7: "] = "
8: "writefln"

// local x = [1, 2, 3, 4, 5];
newarr r2, 5
lc r3, c0
lc r4, c1
lc r5, c2
lc r6, c3
lr r7, c4
setarr r2, 0, 5
movl r1, r2

// for(local i = 0; i < #x
lc r2, c5
len r3, r1
cmp r2, r3
jge 6

//     writefln("x[", i, "] = ", x[i]);
lc r5, c6
movl r6, r2
lc r7, c7
idx r8, r1, r2
precall r3, g8, 1
call r3, 6, 1

// ; i++)
addeq r2, c0
jmp -11


The opcodes: newarr is "new array", it takes the dest register and the size of the array (if known).  lc is load constant (the constant table is unique to each function).  setarr sets a block of registers all at once to several elements of the array, and is only used for array constructors.  movl moves locals.  len gets the length of something (the # operator).  cmp compares two values, and is always followed immediately by jlt, jle, jgt, jge, je, or jne; the cmp-j pair is executed as a single instruction.  idx indexes, and takes the destination register, then the thing to index, and then the index value.  precall-call are also executed as a single instruction and are used for calling a regular function.  If we were making a method call, it would be a method-call pair instead.  precall takes the register of where the function should go, then the source of the function, and a flag (1 or 0) as to whether an explicit context was passed.  "g8" as the source means "the global whose name is stored in constant 8", which is writefln.  The flag is 1, so the interpreter will automatically fill the register after the function, r4, with the function's environment as the 'this' pointer.  (this is 0 when you use the "with" at the beginning of the argument list to specify an explicit context.)  Then call takes the function register to call (always the same as the register in precall), the number of parameters + 1, and the number of return values needed + 1.  This is again, very similar to how Lua does it.  Finally addeq is the += operator, and so we're just adding 1 to i, and jmp jumps back to the comparison of the loop.  Notice that like a real processor, the PC is incremented after the instruction is fetched, so jump offsets are offsets from the instruction _after_ the jump.

Hope that whets your appetite :)


August 02, 2007
Jarrett Billingsley a écrit :
> "Chris Nicholson-Sauls" <ibisbasenji@gmail.com> wrote in message news:f8qrm5$131h$1@digitalmars.com...
>>  ____[ MiniD ]____________________________________
>> |
>> |  local x, y ;
>> |  // bunch of stuff
>> |
>> |  x, y = loadString("
>> |    local x, y = varargs ;
>> |    // do some dynamic stuff with x and y
>> |    return x, y;
>> |  ")(x,y);
>> |_________________________________________________
> 
> Pretty ingenious idea :D 

Well as a workaround for a technical problem, why not, but from a syntactic point of view, it's not very good (that Perl do it this way is a good indication that you should avoid it IMHO).


renoX
August 03, 2007
I don't know how I managed to last this long without investigating what MiniD is.  But it's such a nice idea!  I had begun myself to think of a similar idea.

> MiniD is based entirely on Tango, the alternative standard library for D. MiniD was last built with Tango 0.99.

Considering the nature of Tango, will an application that uses MiniD still be able to use code/libraries relying on Phobos as well?

Stewart. 

August 03, 2007
"Stewart Gordon" <smjg_1998@yahoo.com> wrote in message news:f8vdpq$232c$1@digitalmars.com...
>I don't know how I managed to last this long without investigating what MiniD is.  But it's such a nice idea!  I had begun myself to think of a similar idea.
>
>> MiniD is based entirely on Tango, the alternative standard library for D. MiniD was last built with Tango 0.99.
>
> Considering the nature of Tango, will an application that uses MiniD still be able to use code/libraries relying on Phobos as well?

Tangobos works amazingly well.  I used it while porting MiniD from Phobos to Tango, and had no problems with some parts of the library being in Phobos while others were in Tango.  There are some linking issues with redefinitions of some of the libc functions, but that's easy to fix by commenting out those definitions in the Tangobos headers.

Of course, in a perfect world, there'd be only Tango ;)  But...


August 04, 2007
Jarrett Billingsley wrote:
> After more than a year of brainstorming, writing, re-writing, taking ideas from other languages and generally having a good time, I've come to what I feel is a good place to call it 1.0.
> 
> == What is MiniD? ==
> 
> MiniD is a scripting language written entirely in D, designed around D's features and with D's semantics in mind.  It's based mainly off of Lua and Squirrel, with influences from D and JavaScript.

Is there any chance of support for optional strong typing? That's my main beef with Javascript; the one time I've used it for a production system, it took six hours to do something that would have been fifteen minutes with strong typing (due to a single bug), and I never did accomplish what I set out to do.

At least function arguments should be typed, or typeable, in an object-oriented language. Lua doesn't need strong typing as much because it only has primitives, functions, and tables. But MiniD is object-oriented, and the only alternative to typing is reflection.[1] Maybe some sort of 'where' clause?
function do_stuff(arg1, arg2) where arg2 : ExpectedClass {}

-cbw

[1] And that's rather ugly and long-winded:
if (!is (typeof(argument) == ExpectedClass))
   assert(false);
August 04, 2007
"Christopher Wright" <dhasenan@gmail.com> wrote in message news:f926br$2a08$1@digitalmars.com...
> Is there any chance of support for optional strong typing? That's my main beef with Javascript; the one time I've used it for a production system, it took six hours to do something that would have been fifteen minutes with strong typing (due to a single bug), and I never did accomplish what I set out to do.
>
> At least function arguments should be typed, or typeable, in an
> object-oriented language. Lua doesn't need strong typing as much because
> it only has primitives, functions, and tables. But MiniD is
> object-oriented, and the only alternative to typing is reflection.[1]
> Maybe some sort of 'where' clause?
> function do_stuff(arg1, arg2) where arg2 : ExpectedClass {}

I think you must have been reading my personal design notes :)  Function parameter type constraints is a feature I've been considering for v2.0. They'd look something like this:

function f(x : int, y : !int, z : int | float, w : instanceof Foo) { ... }

x : int means only ints are allowed; y : !int means anything _but_ ints; z : int | float means z can take an int or a float, and w : instanceof Foo means it has to be an instance of class Foo (or any class derived from it).

I also had an idea for "semi-static typing" last year when I was making the transition from static typing to dynamic typing.  Basically variables could be typed (but didn't have to be), but it wouldn't go any further than one "level" of typing deep.  That is, you could do:

int x = 5;
array a = [1, 2, "hi"];

Basic types are not really affected, but aggregate and container types are just typed to the "first level": 'a' can only hold arrays, but there's no way to specify what should be _in_ that array.  Notice that a holds ints and strings.

I'm wondering how feasible this would be to implement.  The static typing all happens at compilation, and then the compiler could emit instructions for optimized operations, i.e. if it knows that two operands of + are ints, it can output an "iadd" instruction which skips dynamic type checking on the operands at runtime and knows that the operands are ints.

Who knows, it could eventually make it in :)  It'd probably be optional in order to largely preserve backwards compatibility, but it probably wouldn't be too much of a departure from the current design.