Dynamic scoping

Developer
Jan 30, 2010 at 8:06 PM
Edited Jan 30, 2010 at 8:08 PM

Hi!

I found that EvaluationContext.TryGetValue() always uses dynamic scoping. Consider the following fragment (in Pascal):

procedure Foo()
var bar: Integer;

  procedure Quux()
  var bar: Integer;
  begin
    bar := 2; { set Quux::bar }
    Baz();
  end;

  procedure Baz()
  begin
    Println(bar); { should be Foo::bar }
  end;

begin
  bar := 1;
  Quux();
end;

In Pascal, Baz() should access "bar" variable from its parent procedure Foo().
But in Irony it uses "bar" from Quux() because call stack looks like Foo() -> Quux() -> Baz().

I think, there should be some flag in a grammar to override this behavior of EvaluationContext for statically scoped languages like C, Pascal, etc.

Jan 30, 2010 at 8:41 PM

I understand your question, but have one for you. 

In your grammar, are you defining a "sub" procedure non-terminal that has a parent procedure non-terminal, or are you just parsing procedures under the same non-terminal.  I believe a grammar issue could give you the same erroneous result as your call stack should be Foo() -> Quux() and Foo() -> Baz().  The scenario Foo() -> Quux() -> Baz() should never be a valid scenario under your grammar, and EvaluationContext should never produce this sort of call stack unless procedure Baz() is a sub procedure of Quux().

Regards,

MindCore

Developer
Jan 30, 2010 at 10:16 PM
Edited Jan 30, 2010 at 10:21 PM

MindCore, this is a pretty valid scenario. Just try to run the snippet with Free Pascal Compiler. Once I have fixed a few typos, it compiles and runs OK (it prints "1" as expected):

procedure Foo();
var bar: Integer;

  procedure Baz();
  begin
    Writeln(bar); { should be Foo::bar }
  end;

  procedure Quux();
  var bar: Integer;
  begin
    bar := 2; { set Quux::bar }
    Baz()
  end;

begin
  bar := 1;
  Quux()
end;

begin
  Foo()
end.

Here is the same code in C# (it runs exactly the same way):

using System;

static class Program
{
  static int bar;

  static void Baz()
  {
    Console.WriteLine(bar);
  }

  static void Quux()
  {
    int bar = 2;
    Baz();
  }

  static void Main()
  {
    bar = 1;
    Quux();
  }
}

If I were writing Pascal grammar I'd use the same non-terminal for nested procedures as they do have the same syntax. Checking whether certain calls are valid or not is a matter of semantics, not syntax.

Coordinator
Jan 31, 2010 at 2:51 AM

Yeah, this is an overlook, I never planned to implement dynamic scoping, it's just the incomplete, draft nature of the interpreter in current version. If you look at the StackFrame class, there are two fields - Caller and Parent, both frames. Parent is a lexical parent, and variable lookup should go thru the chain of lexical parents, not callers. But for this, a proper lexical parent should be set for each function - which means functions should be turned into closures over owner frame, and so on, and on and on... 

It used to work like this in "old" version, but not done yet in the current version. I'm working on interpreter infrastructure currently, so guys - take a break, don't invest much time into coding over existing stuff. Big changes are coming, hopefully they will be final, and everything wil start working properly. Expect it in 2 - 3 weeks.

Sorry for the inconvenience...

 

Jan 31, 2010 at 3:57 AM

Yallie,

I apologize if you miss understood my last post.  I wasn't refuting the validity of the scenario.  I was simply stating that an improper grammar implementation could cause the same erroneous effect.  If your BNF expressions aren't properly configured, the procedures and sub procedures down the line could have the same scoping issue.  It appears that you already have a pretty stable grammar though, so you are in the same boat as I and can't wait to see Roman work his magic.

 

Roman,

Thanks for the heads up on the status of the interpreter and its current state.  Can't wait to get my hands on your next code drop.

Thanks,
MindCore

Developer
Jan 31, 2010 at 10:38 AM
Edited Jan 31, 2010 at 10:55 AM

MindCore, sorry if I misunderstood you. I thought that you're stating that call stack like Foo() -> Quux() -> Baz() in my sample should never be a valid scenario.

Roman, there are plenty of dynamically scoped languages. I don't think you should just remove dynamic scoping.

By the way, I figured out that there are languages that support both dynamic and static scoping. I just wrote this example (in Perl):

#!perl.exe

foo();

sub foo
{
	my $static = "value set by foo()";
	local $dynamic = "value set by foo()";

	quux();
	bar("foo()");

	sub quux
	{
		my $static = "value set by quux()";
		local $dynamic = "value set by quux()";
		bar("quux()");
	}

	sub bar
	{
		my $caller = shift;
		print "
bar() called by: $caller
static scoping:  $static
dynamic scoping: $dynamic
";
	}
}

It outputs the following:

bar() called by: quux()
static scoping:  value set by foo()
dynamic scoping: value set by quux()

bar() called by: foo()
static scoping:  value set by foo()
dynamic scoping: value set by foo()
Coordinator
Feb 1, 2010 at 10:10 PM

Hi

 thanks for the example.

One thing that surprised me is "there are plenty of dynamically scoped languages". Until your post, I was aware of only old, before-Common-Lisp-standard implementations of Lisp, and I think they let it into the standard just to allow implementations to be compatible with old versions. I wouldn't worry about these too much.

From your post, I found out about Perl - quite a surprise for me. What are the others from the "plenty of..."? Wikipedia page on dynamic scoping mentions only Perl and Lisp... Perl in any case is a "weird" language for parsing, it is well known it is not parseable by LALR, only by custom parsers with a lot of custom lookahead tricks. Given this, Perl is also not a good subject for Irony parser, and I wouldn't go for troubles implementing both lexical and dynamic scoping just for perl.

So, what are the others?

thanks

  

Developer
Feb 1, 2010 at 11:58 PM
Edited Feb 2, 2010 at 12:17 AM

The others include APL (still alive), Snobol4, Tcl, TEX, Postscript, Jaskell, scripting languages of TECO and Emacs. Environment variables use dynamic scoping semantics (child process runs within parent's environment) in dos/windows/unix shell languages.

>I wouldn't go for troubles implementing both lexical and dynamic scoping just for perl.

Dynamic scoping (shallow binding version) is already implemented, and it's just a few lines of code. So why not just leave it as is :)

Coordinator
Feb 2, 2010 at 6:22 PM

Thanks for the links! Yeah, now I see it is something to address in implementation. Will put it into design

thanks again!