Jump to content
  • Advertisement
  • entries
    359
  • comments
    237
  • views
    190045

About this blog

Current topic is the development of a toy language to test a few syntax ideas.

Entries in this blog

 

Tangent: Dice, Part 2.

The example code below (plus one semi-colon) now works. A few other things now do not work, and I'm sure to have created a slew of bugs by just hacking stuff until this works. But it should be a net gain I think... questions, criticisms and the such about the last post's code certainly welcome.

I'm probably going to look into cleanup, and some enhanced documentation soon. Also I should get more feedback from a wider variety of programmers. It's hard to judge the readability of things after working with them for a year... Later though. Too much debugging and the brain just goes out to lunch.

Telastyn

Telastyn

 

Tangent: Dice, Part 1.

Got to do a little work on the dice example tonight. To me, it was pretty straightforward to construct the classes/methods to make the tiny DSL beginnings like I wanted. Not sure if that will translate well to other programmers. Found a few bugs doing it; they're more of an impediment at this point than syntax.

This doesn't quite work yet (the extension of int to take a RollOfDice doesn't compile right), but the syntax is there. For feedback, and those interested:


public class RollOfDice{
public delegate Roll()=>int;
public static Random rng = new Random;

protected int minimumValue;
public int MinimumValue{
get{
return(minimumValue);
}
}

protected int maximumValue;
public int MaximumValue{
get{
return(maximumValue);
}
}

public static Create( void -> int rollProcedure, int min, int max) => RollOfDice {
local RollOfDice rtn = new RollOfDice;

rtn.Roll = rollProcedure;
rtn.minimumValue = min;
rtn.maximumValue = max;

return(rtn);
}

public static CreateDie( int sides ) => RollOfDice{

return(
// bug #67,
// static scoping needs full path.

RollOfDice.Create(
()=>int{ return( (RollOfDice.rng.Next(sides))+1 ); },
1,
sides
)
);
}

}
// bug #65, custom initializer is fubar'd
//public RollOfDice d20 = RollOfDice.CreateDie(20);

public RollOfDice d20;
public RollOfDice d12;
public RollOfDice d10;
public RollOfDice d8;
public RollOfDice d6;
public RollOfDice d4;

public static create dice => void {
d20 = RollOfDice.CreateDie(20);
d12 = RollOfDice.CreateDie(12);
d10 = RollOfDice.CreateDie(10);
d8 = RollOfDice.CreateDie(8);
d6 = RollOfDice.CreateDie(6);
d4 = RollOfDice.CreateDie(4);
}

public static roll (RollOfDice die) => int{
return(die.Roll());
}


public class int{
public this (RollOfDice die) => RollOfDice{
local int min = die.MinimumValue * this;
local int max = die.MaximumValue * this;
local void -> int newDie = ()=>int{
local int rtn = 0;

foreach(int count in 0 to this){
rtn = rtn + roll die;
}
return(rtn);
}

return(RollOfDice.Create(newDie,min,max));
}
}


public static main()=>void{
create dice;

print roll 3d6 "\r\n";

foreach( int x in 0 to 10 ){
print roll d20 "\r\n";
}
}



Telastyn

Telastyn

 

Of Dice and Men

Not much Tangent work recently. Social nicety and work anxiety have sapped time and motivation. Whimsy has driven me towards a little example project. This is not a terrible event because I've not done enough stuff to shake out bugs and get a good feel for the language from a 'writing code in it' standpoint. Plus, I don't have many examples; and no good examples that really show off features.

So I've started work on a small dice DSL. Something to allow 2d10 +4 sort of stuff in source. So far it's uncovered two fairly substantial bugs; one blocking, and one that just makes things un-ideal. More to come (slowly) as it gets done.

Telastyn

Telastyn

 

Tangent: Enums

After doing some of my own ruminations on the subject, I made a post to the community at large to get some input/ideas for enums. Most people seem as if they'd be fine with C# enums. I don't think I'd be fine with C# enums in Tangent.

I'm not completely sold on their design yet, but the current basic implementation is fairly Java-style enums. The actual constructs themselves are sugar for some global, constant goose type instances. A individual enum value is a class instance that is a sub-type of the abstract enum class. Since they are just classes, you the programmer will be able to define methods, fields and properties on the enum class and by extension the enum values. And methods will be able to be specialized on a particular enum value, just like any other subtype.

Currently enum values have a sane ToString defined, and the enum class (as well as the global scope) has static properties that return the enum values by name. The values themselves live as static tuple members on the enum class (as you'll see below). That is likely to be hidden or abstracted later. Enumerating through the enums and specifying specializations are not done yet. Other stuff will be driven by suggestion or more likely, personal need when doing example apps.

The current testapp (yes, yes I need to get == working again...):


public enum CardSuit{
values{
club,
spade,
heart,
diamond
}

public foo()=>void{
print "foo.\r\n";
}
}

public static main()=>void{

CardSuit.tuple[0].foo();
CardSuit.diamond.foo();

if( CardSuit.tuple[0] equals CardSuit.tuple[0] ){
print "equality works.\r\n";
}

if( CardSuit.tuple[0] equals CardSuit.tuple[2] ){
}else{
print "inequality works.\r\n";
}

if( CardSuit.club equals CardSuit.club ){
print "equality works.\r\n";
}

if( CardSuit.heart equals CardSuit.diamond ){
}else{
print "inequality works.\r\n";
}

print CardSuit.heart "\r\n";

local CardSuit suit = CardSuit.club;
print suit "\r\n";

suit = CardSuit.diamond;
print suit "\r\n";

suit = spade;
print suit "\r\n";
}




foo.
foo.
equality works.
inequality works.
equality works.
inequality works.
heart
club
diamond
spade


Telastyn

Telastyn

 

Tangent: Arrow.

Had the day off to take care of some errands. I tend to lack motivation, so recharge days are vital. Then I spent a whole lot of time banging my head against stupid bugs and little awkward problems. So not a lot of progress...

Then I got fed up with it and implemented new stuff. A whole 5 minutes later, and the arrow operator exists. The arrow operator (->) is a type-expression operator. It takes two types and creates a static method signature as you might expect:


public static test(int x) => void {
print x;
}

public static main() => void{
local int -> void op = test;

op(5); // 5
}





Or, a more complex example which is still not as elegant as list comprehensions...


public static elements in (IEnumerableint> collection) where (int -> bool predicate) => yieldsint> {

foreach( int x in collection ){
if( predicate x ){
yield(x);
}
}
}

public static (int min) to (int max) => yieldsint> {
local int counter = min;

while( counter yield(counter);
counter = counter + 1;
}
}

public static (int n) is even => bool {
return( n%2 equals 0 );
}

public static main() => void{

foreach( int x in elements in 3 to 10 where (int n) => bool{ return( n is even ); } ){
print x;
}
}






Which would be sweet if it worked. There's a bug with yielding from within an if block which the last hour has not fully diagnosed. Still; a few people were looking for more complex examples, and there you go.

[edit: 90 minutes, no progress on the bug. It is a problem for blocks not resetting their execution pointers as they should after a yieldy block is restarted. Should be more simple when I can think again. It is the same problem I had earlier with nested loops and yield blocks. if stuff is a little more nuanced since you don't know when/if it's restarted via loop.

Definitely makes the .NET switch/jump hack look a lot more elegant.]

[edit2: the second example now works properly.]

Telastyn

Telastyn

 

Links and progress

Happened across a nice (new) article by Bruce Eckel that I think very aptly describes C++ (and Java) in today's world. Always refreshing to see someone who is published explain something more clearly and concisely than I could.

I also happened across the Fan programming language a few weeks ago, but a reference to it in Eckel's blog reminded me to re-post here. It fits into that same sort of 'something like Java/C# but with first class functions and less rigid type structure' family of languages that Scala headlines and I'd like Tangent to be.


Speaking of which, the big bug problems of late last week have allowed for a flurry of smaller bugfixes and positive test results today. Most important of these at the moment is the ability for generators to be infix operators. Since there's no for statement, there needs to be a range generator to cause the same behavior. At the moment, I'm likely to use to as that operator with something like up to to be the exclusive version. ie


foreach( int x in 1 to 5 ){
print x; // 12345
}

foreach( int x in 1 up to 5 ){
print x; // 1234
}



Much more Tangentlike than range(1,6); or even 1..6

Also tested were some of the simple generic method syntax. I unfortunately will probably need to break the syntax that is there (away from C# syntax) in order to do specialization in a non-psychotic manner. Though probably not soon. I've yet to actually make generic parameters visible in methods, making them kinda useless.

Telastyn

Telastyn

 

Victory!

No Tangent work today.

Spent the evening dominating a mtg draft tournament with one card.

Telastyn

Telastyn

 

Tangent: Mines!

I did a quick little update to fix infix operators that yield, and everything went to hell. I mean Console.WriteLine stopped working. A tiny program that used to take half a second to compile is now taking 11 minutes, and pukes null reference exceptions...

I fixed the first problem, and the second might just be caused by the import grabbing almost all of the .NET libraries rather than 8 mostly empty classes (and then me dealing with them stupidly). The third is... something.

Not a very productive night.

[edit: Helped compile times. (20s vs 11m30s) Constant is better than linear, mmmkay.]

Telastyn

Telastyn

 

Tangent: Improved .NET Imports

Minor update. The built-in types now are imported fully from whatever .NET assemblies you have rather than hard-coded into the language. The current importer can deal with any type that has a default constructor, or is static/abstract/etc, or is System.String. It imports the default constructor and any (instance or static) fields, methods, indexers and properties on the type that are comprised of only types it can deal with.

Currently it does not look at all to overloaded operators, enums, or delegates (it might do delegates, but not correctly if it does). It will handle generic types, but not any constraints on them (and statics on generic types prolly don't work).

And non-standard constructors are out.


Still, a step in the right direction. Mostly, this enhancement will allow string manipulation which is important for demo/example programs.

Telastyn

Telastyn

 

Tangent: Little fixes.

A number of little bug fixes tonight. A little added functionality.

This used to be broken, but now works:


public class int{
public this is even => bool { ... }
}




Name resolution on .NET types failed if I tried to do it early on in compilation. (fixed)
Making a method group of 'this' methods didn't properly include the instance when invoked (fixed)

And now that statics work, I added the capability to the .NET importer to try and import static members of a type. It doesn't work yet for the built in types (int/string/float). Mostly I did it to get the Console goodness available. The importer can't deal with the param modifier so only the one arg Console.WriteLines get into the environment. Also missing is operator overloads. Still, a lot more programs become viable now that Tangent apps can read user input (though without meaningful string manipulation, it's still rather hamstrung).

Telastyn

Telastyn

 

Tangent: Statics

Had a little extra time tonight to work on static methods and variables, and now they work:


public class foo{
public string bar = "moo.";

public static int baz = 6;
public static genFoo(string initValue) => foo {
local foo rtn = new foo;

rtn.bar = initValue;
return(rtn);
}
}

public static main()=>void{
local foo Foo = new foo;


Foo = foo.genFoo("bleat.");

print Foo.bar; // bleat.

print foo.baz; // 6
}



This turned out to be one of those things that looks simple and should be simple, but is just a pain to implement. Even though it's 'working' now, I'm marginally afraid to do anything more complex than the example code. I can practically hear the bugs crawling about in the hackery used to get it working.

That leaves 'this' methods and generics (and the inevitable debuggery from this mess) in this batch of cleanup. Far too much. I must continue the focus on making little hard examples work. One tiny step at a time.

Telastyn

Telastyn

 

Tangent: Invisible Progress

Not much to report on the Tangent front. Partially it's because of lack of time, partially because it's in a bug-fix cycle. A lot of the work is making things that haven't even made it into a release yet (or were invisibly borked in the last release) work properly. And discovering more things that don't work of course. For the last week or so I've created 2 bugs for every 1 I fixed.

The two notable fixes:


public class foo{
public static gen() => foo { return new foo; }
}

public static main()=>void {
local foo FooInstance = new foo;

FooInstance = FooInstance.gen();
}



This now does not work; it used to. foo.gen() still doesn't work, but that's another problem for another day.


public (int x) is even => bool {
return( x % 2 == 0 );
}



This does work now. It's not idiomatic, this is probably the better way of doing that:


// Since all classes are treated as partial
public class int{
public this is even => bool { ... }
}



The first makes an operator is that takes 'even' and an int. The second is a phrase that starts with int. The second is likely more useful and I should maybe make the code generation for the first do the second. Even though the second doesn't work at the moment...

Like I said fix one bug, find 2.

Telastyn

Telastyn

 

Tangent: Method Tests Part 3

A few of the fruits from the last big refactoring to show today. Phrases now can act as generators, and delegates can nicely deal with generators:



public static nums() => yieldsint>{ yield(0); yield(1); yield(2); }
public static some nums => yieldsint>{ yield(0); yield(1); yield(2); }
public static good many nums => yieldsint>{ yield(0); yield(1); yield(2); }
//
// bug #39
//public static (int min) to (int max) => yields{ int x = min; do{ yield(x); x = x+1;}while(x

public static delegate yieldDel() => yieldsint>;


public static main()=>void{

foreach(int x in nums()){
print x;
}

foreach(int x in some nums){
print x;
}

foreach(int x in good many nums){
print x;
}

yieldDel = nums;
foreach(int x in yieldDel()){
print x;
}
}




012012012012


Infix methods as generators don't quite work yet, but at least I identified that. They'll work soon enough.

Telastyn

Telastyn

 

Tangent: Method Tests Part 2

Not much time for hobby this weekend. I managed to setup some delegates and make sure they work like they're supposed to. The example code, which includes infix delegates and a quick example of the partial application built into the language:


public static foo() => void{ print "foo.";}
public static bar(string s) => void{ print s;}
public static baz(string s, int i) => void{ print s i;}
public static phrase(string s) (int i) => void{ print s i;}
public static how now brown cow => void{ print "MOO!";}
public static test (int lhs) times (int rhs) => int{ return lhs * rhs; }
public static (int lhs) op (int rhs) => int{ return lhs * rhs; }
public static (string lhs) * (string rhs) => string{ return lhs + rhs; }
public static (string lhs) ++ (string rhs) => string{ return lhs + rhs; }
public static (int x) a b => int{ return x+5; }

public static delegate voidDel() => void;
public static delegate (string lhs) opDel (string rhs) => string;
public static delegate what now brown cow => void;
public static delegate cowDelegate cow => void;

public static main()=>void{
foo();
bar "moo.";
baz ("bleat.",6);
phrase "bleat." 42;

how now brown cow;

print test 4 times 6;
print 2 op 3;
print "moose:" * " umm. moose.";
print "moose:" ++ " umm. moose.";

//print 5 a b; // bug #37

voidDel = foo;
voidDel();

opDel = +;
print "moo" opDel "cow";

opDel = ++;
print "bleat" opDel "sheep";

what = how;
what now brown cow;

cowDelegate = how now brown;
cowDelegate cow;

voidDel = ()=>void{ print "llllama."; };
voidDel();
}


Telastyn

Telastyn

 

Tangent: Method Tests Part 1

The first little batch of method tests/examples are done and working:


public static foo() => void{ print "foo.";}
public static bar(string s) => void{ print s;}
public static baz(string s, int i) => void{ print s i;}
public static phrase(string s) (int i) => void{ print s i;}
public static how now brown cow => void{ print "MOO!";}
public static test (int lhs) times (int rhs) => int{ return lhs * rhs; }
public static (int lhs) op (int rhs) => int{ return lhs * rhs; }
public static (string lhs) * (string rhs) => string{ return lhs + rhs; }
public static (string lhs) ++ (string rhs) => string{ return lhs + rhs; }

public static main()=>void{
foo();
bar "moo.";
baz ("bleat.",6);
phrase "bleat." 42;

how now brown cow;

print test 4 times 6;
print 2 op 3;
print "moose:" * " umm. moose.";
print "moose:" ++ " umm. moose.";
}




The only perhaps noteworthy bit is the last method, which until now never worked in source. The technical construction of that is:


+ = (+ -> (infix (string,string) -> string))


Which the language does automatically and cleans up for you. A ton more conditions to check and bugs to fix. After that I think I'm going to work on more in depth documentation of methods, so get questions in and suggestions for the style/format now so the docs can best help you the mildly interested programmer.

Telastyn

Telastyn

 

Tangent: Phrase Refactor, Part 1

I have basic phrases working again with the new syntax:


public static foo()=>void{
print "foo.";
}

public static bar(string s)=>void{
print s;
}

public static baz(string s, int i)=>void{
print s i;
}

public static phrase(string s) (int i)=>void{
print s i;
}

public static main()=>void{
foo();
bar "moo.";
baz ("bleat.",6);
phrase "bleat." 42;
}



I'm not sure I did much to make the code more maintainable. There is less copy/paste for sure, but it's a little more convoluted and there's still enough to cause a problem. Theoretically more things should work now, like infix phrases, delegate phrases and some other goodies. But if I start down that bug strewn road I might not get to sleep.

Next bit of work will be a (mildly) rigorous set of tests to make sure all of the functionality is there and to perhaps help demonstrate to curious programmers what phrases are, how they're structured and what they look like. Though more likely they'll end up being a bar graph of how much work I have yet to do.

Telastyn

Telastyn

 

FLU!

Not dead. Mostly. Last week was sacrificed to the flu. Re-working a large/nasty method continues. Operators work again, and I found a few little bugs that never worked in the first place. Operators are now auto-detected by where you put the name of the method in the phrase:


foo(int x, int y) => int {} // foo - not an infix operator.

(int x) foo (int y) => int {} // foo - an infix operator.


Phrase work is partly done. I need to now do the code generation to do the automatic partial application. Hopefully it'll be cleaner and more consistent this time. More later in the week.

Telastyn

Telastyn

 

Tangent: Explicit Symbols.

So much for all that time during the week I was going to have to work on the Tangent cleanup... I did get a little time this morning and added a small feature that's going to be needed with the changes to how method declarations are dealt with.

In 0.25 you can specify literal identifiers to be part of a method/phrase, ie:


public static void foo bar{...}


To invoke the method you must have the identifier foo next to bar (regardless of any declarations for bar). The small feature is to expand that to a subset of symbols generally used for operations. ie


public static foo ! => void {...}
public static +> => void {...}


Generally these things won't return void and will be used in longer phrases:


public static (string lhs) ++ (string rhs) => string { return( lhs.concat(rhs); }

public static (int lhs) ** (int rhs) => int { return(Math.exp(lhs,rhs)); }


Technically, these are 4 element phrases. + op that takes a string and a + literal and returns a method that takes a string and returns a string.

Aside: though from a design standpoint, it would be better to define a simple method ++ that returns a more verbose operator that does concatenation. Otherwise you can't pass simply '++' to a delegate. Perhaps I'll change the code to do that automatically...

While this sort of thing can easily be used to dominate the first annual obfuscated Tangent contest, it also provides people who are going to develop DSL's within the language to define symbolic operators in a nice readable fashion without me specifically telling them what can and cannot be utilized (beyond a few in-grained operators).

Telastyn

Telastyn

 

Tangent: Smash Syntax! (part 1)

Before we get to coding work, news and opinions. My workplace laid off a small percentage of its workforce. Mostly the most expensive workforce (definitely not me!). Unfortunately that tends to be the most skilled of the workforce. How a company can continue to survive such clearly braindead executive decisions is mildly baffling.

The kindle2 came out recently, and looks kinda nifty. I looked at the first one, but was scared off by the price mostly. I don't really want a kindle. I want an iPhone the size of the kindle. Who cares if I can't put it to my ear to hold a conversation? The only time I'd use it as a phone is to order pizza...


Now to work. I planned out phrase re-implementation earlier in the week. A good 150 lines of comments; mostly with process description, a little psuedo code. Tonight I went through and broke almost all of the language with a breaking change to the syntax. The position of the return type is being shifted from C style to functional style:


public static void main(){...}

// TO

public static main()=>void {...}


Despite all evidence to the contrary, I am not an ApochPiQ wannabe (even though he's doing great work, and you should go look). No, it's been something I've been considering for a while. There are 3 or so reasons off the top of my head.

1. Eventually I'd like to support type expressions. It gets a lot harder to unambiguously determine the return type when it's not a simple identifier. The explicit => aids that.

2. This causes the syntax to be very similar to the lambda syntax. Some unification there might allow for decreased code duplication and a better system overall.

3. This will allow more parameter flexibility. Operators for example will be able to done in order.


// Pre-change
public static operator int plus(int lhs, int rhs){...}

// Post-change
public static operator (int lhs) + (int rhs) => int {...}


It also means the 'operator' keyword is extraneous. If the first argument is an identifier or symbol, it's a simple phrase. If it's the second, then a binary operator with that as the name/hook. I'm leaving that keyword in to be explicit, but am looking for feedback (hint.) if it's necessary still.

Now it's time to add the symbols as allowable syntax and then phrase reimplementation. Until then, nothing beyond unary operations work...

Telastyn

Telastyn

 

Detour with a mop.

My work at making generators able to be declared as phrases took a little detour today when I realized that I couldn't read the build method code anymore. Making phrases work was a good deal of hackery; copy/paste/edit/tweak until it worked. Type parameterization and yield got added onto that. The result was a 1010 line recursive method that took 13 parameters. [dead]

And that's not even the part of code that generates runtime tokens. It's just the code to make a type definition for a method declaration. I'd been meaning to clean it up, and now's a good a time as any. This should also allow type parameterization in phrases (which was missing) and phrase yield methods too. I broke the 'simple' case of non-phrase method into about a half dozen smaller methods. It's still not quite readable, but a lot better.

That leaves the other three parts of the big method: the first part of a phrase, the last part of a phrase, and the stuff in between. Then some added cleanup (or perhaps a second iteration of refactoring).

Ideally it'll get done for next weekend, leaving debugging the new features.

Telastyn

Telastyn

 

Workday: Victory (mostly)

(see below for the problem in detail)

The bug has been squashed. The problem was in the loop element; during the upgrade of them to properly deal with yield-blocks I made a mistake in how the predicate is processed. It was doing:


while( predicate && I'm not supposed to yield control ){
do stuff();
}


The problem was with the foreach loop, which reduces to while( itr.MoveNext()){...}. So when the loop element checked, it ran MoveNext before doing the yield check, causing the duplication behavior. A simple reversal of the conditions and the code below gives 01234 as it should.

I also fixed a bug where a yield at the end of a block caused the block to return rather than yield. Ended up being a problem during the restart; the 'end of statements' condition wasn't distinguishing between a restart there and reaching there due to simply running out of statements.

Tomorrow should be the matter of turning take(int, collection) into take int from collection

Telastyn

Telastyn

 

Printf debugging for the win.

After 40 minutes in a debugger tracing through 'bytecode' (not really bytecode, but elemental runtime stuff), I found that the inner generator was at 1 by the time main printed 0. So somewhere the inner generator was being run twice before the outer generator actually returned control to main.

Make sense? No; of course not. And 'somewhere' isn't useful for problem solving. So I resorted to doing printf debugging in the language itself. 3 minutes later and I've a much clearer picture of at least where the problem occurs:


public static yieldsint> take(int amount, IEnumerableint> fromCollection){
local int ix = 0;
foreach( int rtn in fromCollection ){
print "start of take foreach.\r\n";
if( !(ix print "before take return.\r\n";
return();
}
print "before take yield. returning rtn: " rtn " \r\n";
yield rtn;
print "after take yield. rtn is still: " rtn " \r\n";
ix = ix + 1;
print "end of take foreach.\r\n";
}
print "exit take.\r\n";
}

public static yieldsint> allInt(){
local int i = 0;
while(true){
print "before allInt Yield. yielding i: " i " \r\n";
yield i;
print "after allInt Yield. i is still: " i " \r\n";
i = i + 1;
}
}

public static void main(){
foreach( int x in take(5,allInt()) ){
print "X: " x " \r\n";
}
}




before allInt Yield. yielding i: 0
start of take foreach.
before take yield. returning rtn: 0
after allInt Yield. i is still: 0
before allInt Yield. yielding i: 1
X: 0
*snip*


Yuck. The core of the problem is at output line 3->4. take's yield is supposed to return to main so the result gets printed out. Instead, it's moving the execution pointer into allInt and causing that to run again. It does it after setting the return value though, since 0 is eventually printed. So probably in the foreach block/loop, it thinks it's supposed to restart something rather than exiting back to main.

Now to find out why...



Telastyn

Telastyn

 

Day of work: Entry 1

After a late awakening, I set into work on Tangent. As a warmup, I added support for the modulo operator (%). Takes two ints, returns the remainder; no big deal. Then came adding an automatic return at the end of yield blocks so the last value wasn't yielded twice. No problem.

I then figured that I better break the take implementation into two parts since there's two things to test. One being using yield methods with parameters, and the second being making phrase-y yield methods. So, the un-phrased test of take:


public static yieldsint> take(int amount, IEnumerableint> fromCollection){
local int ix = 0;
foreach( int rtn in fromCollection ){
if( !(ix return();
}
yield rtn;
ix = ix + 1;
}
}

public static yieldsint> allInt(){
local int i = 0;
while(true){
yield i;
i = i + 1;
}
}

public static void main(){
foreach( int x in take(5,allInt()) ){
print x;
}
}





As comes as no real surprise, it didn't work.

First problem was an error where the runtime was trying to access Param[1] when it didn't exist. Tracked that down to the take yield method. The anonymous enumerator didn't have amount or fromCollection so the name resolver found a higher level method, which was crap. Turns out the variables I was using to set the members of the anonymous enumerator weren't what I thought they were. Oops.

Second problem was an error where the runtime was looking for .amount and dying. Makes sense, it should've resolved to this.amount. Turns out that the invocation setup for the yieldy method was being dumb and treating it like a static method.

Third problem was an infinite loop. Tracked that down to the inner loop of take. It wasn't exiting the loop, even though the conditional was processed correctly. It seems I was a little incomplete in my updating loops to be yield-block aware. The foreach loop wasn't detecting the yield 'pause' correctly, so just looped again.

And then the code compiled and ran! It spit out: 02468

Doh. I am going to take a break before trying to figure what wildly convoluted stupidity I've managed to cause that sort of error.

Telastyn

Telastyn

 

Weekend work

I've unfortunately been busy the past week with social nicety and recouping from yield. I fixed up a little left over bug with it, and have (nearly) the entire weekend to myself for Tangent work. I added a wiki entry for coming features in the next release (and linked in the journal header).

First step is to get take working. Then I'd like to polish off a few little example apps to discover bugs in the (mildly neglected) OO side of the language. That should provide more than enough bugs or missing things to keep me busy.

Telastyn

Telastyn

 

Tangent: Yield, part 4

Tonight I had a tiny bit of spare time to devote to tracking down the yield bug. Turned out to be three little bugs. The first was a mistake in pulling the stackframes off during the actual yield call. It kept the top frame pointing to itself for restart rather than just letting it restart. The second was a missing part of while loops. If the loop knew it could yield, it didn't reset the execution pointer when it was time to restart the loop. The third was improperly handling the 'end of method' implicit return. That causes the method to run 1 extra time before signaling 'done' in some cases.

The first two have been fixed, the third hasn't yet. I'll need to add that implied return statement or some workaround that detects it was hit and change behaviors.


But the target example code has been reached, so yield is now considered functional (if yet in need of polish). Behold, a simple Fibonacci program:


public yieldsint> fib(){
local int a = 0;
local int b = 1;
local int c;

yield(a);
yield(b);
while(c 100){
c = a + b;
yield(c);
a = b;
b = c;
}
return();
}

public static void main(){
foreach(int i in fib()){
print i " ";
}
}




The next test I think will involve making a functional language style take. That will involve testing yield generators with parameters, and phrase style yields. Eventually I want something like

foreach( int i in take 5 from the Fibonacci Sequence ){ ... }

to do what it means (possibly replacing 'take' with something more descriptive).

Telastyn

Telastyn

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!