Assorted Project 2 Notes

We'll be collecting interesting newsgroup responses, assorted hints, and various documentation in this file.

  1. Using unpyth to see static semantics
  2. Examining and comparing versions of staff files.
  3. Lecture #26: sample code for variable declarations
  4. Handling primitive types
  5. Variable attributes
  6. Sharing a PRCS repository
  7. Creating semantic delegates
  8. Handling attributes
  9. Classes and inheritance
  10. Various scope rules
  11. Native function calls
  12. Type returned comparison operators

Notes

Using unpyth to see static semantics

We've expanded the `unpyth' command to allow you to see how your semantic processing is doing. We'll be using these features of unpyth to test your solutions.

To see the declarations you have attached to IdNodes, use the command

   unpyth --decls FILE.AST
where FILE.AST is the file produced by your semantic analyzer. This will produce output that looks like this:
    native class Any#0: ...

    native class Unit#1: ...

    etc.

    a#7 = 3
    y#8 = 2
    z#9 = 3
    def f#10 (a#11, b#12):
       y#13 = a#11 + b#12 + z#9
       return y#13
    Declarations:
    #0: AnyType
    #1: UnitType
    etc.
    #7: VarDecl type: Any;
    #8: VarDecl type: Any;
    #9: VarDecl type: Any;
    #10: FuncDecl type: (Any, Any)->Any;
    #11: VarDecl inside #10; type: Any;
    #12: VarDecl inside #10; type: Any;
    #13: VarDecl inside #10; type: Any;

To see the types that your analyzer has attached to expressions, use

   unpyth --types FILE.AST
which (when your analyzer is fully working) should produce something like this:
 
    a: Int
    a = 3
    b: String
    b = "abc"
    f: Int -> Int
    def f (x): return x +::Int 1
    q = a +::Int 3
    b = b +::String "x"
    c = f (a)::Int
The idea here is that `::TYPE' decorates function calls, operators, and a few other things. You will also see decorations when types are "unexpected". In the example above, the program gave all the integer literals the type Int. Had it not done so, you might see this instead:
   a: Int
   a = 3::Any
The notation "::?" indicates a null value for the type.

In all cases, you can see declarations and types for the native classes (which your analyzer sees as NativeClassDeclNodes) with the '--native' option:

   unpyth --decls --native FILE.AST

Examining and comparing versions of staff files.

The new command "staff-prcs" will allow you to look at all versions of the staff project files, including lib/java and the skeleton files for Project #2.

For your convenience, we have specifialized this command specifically for this project. Here are some typical things you might want to do:

Handling primitive types.

The addPrimitiveType method in Type is called for you; you needn't do so. The only serious difference in processing primitive types---represented in the program text as "native classes"---and ordinary classes, therefore, is that for native classes, you get the Decl by either calling Type.getPrimitiveType or (probably easier) by calling .getPrimType() on the native class AST. For ordinary classes, you have to create a ClassDeclType when you process the ClassDeclNode.

In both cases, the name of the class (or native class) should thenceforth be mapped to the Decl that you fetched or created. As you can see, Type is a subtype of Decl, so that the Decl for a class or native class C doubles as the type that is denoted when you use C in a type declaration. That is, when you see

class Thing:
   etc.

X : (String, Thing) -> Any
all of the IdNodes for Thing (actually, of course, their semantic delegates) should point to the same ClassDeclType.

Finally, for both a native class or ordinary class, you should add all the Decls for members (attributes) of that class to its Decl (using .addAttribute).

Variable Attributes.

Q. For proj2, do we need to note that when accessing its own variables, the class method needs to access them with self.variable?

A. Yep. Actually it's quite easy. In Python (and Pyth), each variable attribute is both an instance variable AND a class variable. Consider:

    class foo:
        x = 3
        y = x   # OK
        def bar (self):
            print y      # ILLEGAL
            print self.y # OK
                

    a = foo ()
    a.x = 19
    print foo.x  # prints 3
    print a.x    # prints 19

Compiler-wise, this is all easy. Just make sure that the enclosing declarative region inside bar is the global region, NOT the class foo.

Sharing a PRCS repository

A student inquired:
You recommended that we use a versioning system (specifically PRCS). And I've gotten PRCS to work correctly, but I'm wondering how you can configure it so that only myself and my partner can access the repository (or the project) without making it visible (and editable) for everyone in the class.
One option is to use CVS with ssh instead. To use PRCS, if we have student logins A and B, and A wishes to house the repository, making it available to B, you can use the following procedure
     In A:
    
     # The repository is named SECRET_PASSWORD (replace with a secret name,
     # shared with B only).
     setenv PRCS_REPOSITORY SECRET_PASSWORD
     # Allow world r/w access to this repository
     prcs admin access
     # At this point, you get an interactive dialog that includes:
      prcs: Setting access permissions for repository foo/.
      prcs: Repository entry owner:  cs164-aa
      prcs: Repository entry group:  cs164
      prcs: Repository entry access: 
      prcs: Owner:                   read/write
      prcs: Group:                   no permission
      prcs: Other:                   no permission
      Enter a group[cs164]             
      # Respond with just carriage return
      Group members' access, select(rwnq?)[n]
      # Respond "w"
      Other users' access, select(rnwq?)[n]
      # Respond "w"
Now both partners, A and B do this:
      # In ~/.local_cshrc file, put
      setenv PRCS_REPOSITORY ~A/SECRET_PASSWORD

      # Also, of course, make sure that .local_cshrc is protected:
      chmod go= ~/.local_cshrc
The procedure above assumes that A's home directory has the default protection, rwx--x--x, so that nobody can list its contents to see the name of the repository, but can read files in it if they know the names. If A wants to make his home directory readable, he should instead use
	mkdir ~/secret
	chmod go=x ~/secret
and modify the setenvs to
	setenv PRCS_REPOSITORY ~A/secret/SECRET_PASSWORD
Unfortunately, there are holes in this procedure (I won't tell you what, and I will spay or neuter, as appropriate, anyone who posts them), but it will do for state-government work.

Creating semantic delegates

To make sure our framework can properly invoke your semantic delegate types, make sure that you include a constructor such as that below:
   class ProgramSem extends StaticSemantics {
       ProgramSem (AST source) {
          super (source);
       }
       ...

The createDelegate method in SemanticBase uses a constructor with this signature to create a delegate. Do not change the formal type from AST to a more specific type.

Handling attributes

Q: Where do we process x.y?

A: Attributes are rather tricky. Clearly, you can't completely handle the case above until you know the type of x. Therefore, assigning a Decl to y has to wait until after you have decided on the types of all identifiers, which means after resolving other identifiers, handling "ID: TYPE" declarations, and attaching types to the formal parameters of functions.

So, you'll need yet another pass in which you process each reference SOMETHING.y by first computing the type, T, of SOMETHING (using still another recursive method!) and then looking up y in type T. If you do not find some kind of attribute Decl, simply use the null Decl as the decl value for y; otherwise, use the Decl for the attribute you find (VarAttrDecl or MethodDecl). Specifically:

Classes and inheritance

The statements concerning inherited attributes and the like in the project handout are not quite right. The truth actually simplifies your task, so here it is:

  1. An inheritance clause in a class must reference a class defined PREVIOUSLY in the program. Hence, there may not be a circular chain of inheritance (a class doesn't occur previous to itself).
  2. When processing the body of a class (looking for decls and the like), IGNORE the parent class altogether. In full Python, for example, this works:
           class A:
               a = 3
               b = a + 2
    
    but this does not:
           class A:
               a = 3
    
           class B(A):
               b = a + 2     # Error: a not defined.
    
    So let's us do the same.

    On the other hand, once the class is complete and you need to look up 'a' in something whose type is declared to be 'B', you will need to follow the parent-class chain.

  3. I haven't really said anything about the type rules for overriding inherited attributes, so we can't really test them all that hard. But if you want to do it right, check the following (somewhere):

    If class B inherits from A, then:

    1. If 'x' is a variable attribute in A (i.e., defined by assignment), it may not be def'ed in B. If it is def'ed in A, it may not be assigned to in B.
    2. The static type of a variable attribute in A should be the same as in B.
    3. The static type of a method def'ed in A and overridden in B should have the same number of parameters, should have the same return type, and should have the same parameter types, except for the first (self) parameter, which should have type B in B and A in A.

Various scope rules

Native function calls

Type returned comparison operators

Comparison operators in Pyth and Python are tricky. Consider
   x: String
   y: String
   z: String
   x < y < z
What type should you ascribe to the "<" operators? According to Python, a comparison in isolation yields a boolean value, which we are representing instead as the Ints 0 and 1. However, the meaning of the whole comparison is that if x < y, it is the (String) value of y, and not the Int value of the comparison, that is compared to z. So you could argue that the type of the first "<" could be either String or Int.

Let us declare by fiat that the type of all the "<" operators here is Int.


[CS164 Home Page]

Page was last modified on Tue Apr 5 15:40:27 2005.
Address comments and questions to cs164@eecs.berkeley.edu