February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Semantic Checker (Part I)
Now
, the coding (and the fun) begins.
February 26, 2002 — CSE 131A — Winter, 2002
Your Parser
• fix:
– parser integration – main statements – i/o statments
• don't worry about:
– error recovery (possibly stripping those rules out)
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Last Part of 131a
• You should get familiar with the semantic checker spec, the Oberon spec, and your CUP/yacc files. You should not need to memorize anything.
• You should skim chapters 5 and 7 during the last 3 weeks before the final exam (which is in Warren Lecture Hall 2001). Ch. 6 is mostly next quarter. • Project is due on Friday night of 10th week, not
Sunday night, because your final is on a Monday.
What is semantic checking?
• Syntax checking (parsing) made sure that
the Oberon code fit a given grammatical
structure.
• Semantic checking makes certain that the
words used in a given piece of Oberon code
make sense when used together.
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Our Semantic Checker (Pt. I)
• Scoping & visibility
• Validation according to language spec
• Also, error suppression
February 26, 2002 — CSE 131A — Winter, 2002
Semantic Checks
• undeclared identifiers • redeclared identifiers • undeclared procedures • undeclared functions • illegal type • illegal designator (RHS)• illegally exported type • illegal import
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Oberon Features
• Named Types
• Modules
• Exports
• Imports
• Records
• Pointers
• Scoping/Visibility
Named Types in Oberon
• Like typedefs in C++. Or like an alias.
• example:
– TYPE SmallInt = INTEGER; – VAR x : SmallInt;
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Modules in Oberon
• Collection of vars, constants, types, and
procedures defined in one unit. Like a
library.
• Components in a module may become
public by being
exported
.
• We do not specifically have the
public
and
private
keywords, but we do use the
concept.
February 26, 2002 — CSE 131A — Winter, 2002
Exports in Oberon
• To define something as exported (public), use * after the name. Example:
MODULE x;
VAR y* : INTEGER;
• Gets parsed from IdentDef:
IdentDef -> T_ID
| T_ID T_STAR /* exported */
| T_ID T_MINUS /* read-only -- we won't do this */
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Module Example
MODULE MyStack;
TYPE Stack* = RECORD ...
CONST MaxStackSize :=10; (* private *) PROCEDURE Initialize* () ...
BEGIN
(* constructor code *) END MyStack;
VAR A: Stack; (* Error: Stack undefined *) VAR B: MyStack.Stack; (* okay *)
Relating to the Grammar...
When B gets parsed,
VarDecl -> T_VAR IdList : Type
..the Type derives to QualIdent, which then
does:
QualIdent -> T_ID T_DOT T_ID
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Imports
• As with the need for EXPORTs, Modules
have their own scope and nothing is visible
inside them unless specifically IMPORTed.
February 26, 2002 — CSE 131A — Winter, 2002
Problem with Modules
• Major problem with Modules: Only one
instance of the module -- the one defined.
• It's not possible to have:
VAR a, b : MyStack;
• ...since Module is not a type. Oberon was
around before classes, but it does have
type-bound Records.
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Records
• Like structs in C++ -- classes defined without member functions.
TYPE Student = RECORD
id : INTEGER; midterm : REAL;
END;
VAR x : Student;
x.id = 3; (* use . for records, like Java/C++ *) • But this does not support methods/procedures...
Type-Bound Procedures
• Procedures which are attached to a record
type and only may be called from an
instance of that record type. This is done
through OptReceiver, which is in the
ProcDecl rule:
PROCEDURE (me:Student) Initialize (id : INTEGER);
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Type-Bound Procedures
PROCEDURE (me:Student) Initialize (id : INTEGER);
• Second ID is the record type.
• First ID represents the instance of the variable which called this procedure ("receiver
variable"). Like the predefined "this" in C++/Java, with two exceptions:
1. The receiver variable in Oberon is user-defined. 2. Use of the receiver variable is required to get the
record's fields (which is optional in C++/Java)
February 26, 2002 — CSE 131A — Winter, 2002
More Receiver Variables
C++:
member =3;
this->member = 3; /* Both are the same */
Oberon:
me.id :=123; (* Okay *)
id := 123; (* Error -- doesn't know who id belongs to *)
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Receiver Variables: In Action
PROCEDURE (me: Student) Initialize (id : INTEGER);
BEGIN
me.id := id; (* "me" represents the variable used to call the procedure *)
me.midterm :=0;
END Initialize;
VAR x,y :Student;
x.Initialize(123); (* Sets x.id to be 123. Like saying: Initialize (x,123); *) y.Initialize(222); (* Sets y.id to be 222 *)
Pointers
• Oberon only allows pointers to record types,
but we won't do that until project 4.
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Scoping/Visibility
• Most languages support scoping: identifiers
declared within a scope are not visible
outside the scope.
• Within a procedure's BEGIN/END or any
BEGIN/END variables defined within that
block are not visible outisde the block.
February 26, 2002 — CSE 131A — Winter, 2002
C++ Scoping Example
int x;
void f() {
int y;
if (...) {
int z;
}
}
x,y,z visible x,y visible x visibleFebruary 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Oberon Scopes
• Modules, procedures, and the main program
all define a scope. In the language you will
see that the VarDecl rule is only called for
those three items.
• The grammar does not allow items
var/type/const/proc to be declared in just
any BEGIN/END block. If it did, it would
have to be included in the StmtList rule.
Semantic Checker (Pt. II)
• Type Checking
• Type Casting
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
SemCheck: To Do
• Defining the scopes of a program. You
need to integrate the parent's scope with a
child's scope.
February 26, 2002 — CSE 131A — Winter, 2002
SemCheck: ToDo (cont.)
• Handle storing declarations in a given scope. • Example: Store the fact that "x" is defined in a
given procedure.
• Validate what identifiers are visible in the current scope during statments.
• Example: Validate that "x" is really defined in the procedure of the parent scope (enclosing
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
SemCheck: ToDo (cont.)
• Validate that the items used in the grammar
rules are used according to the Oberon spec.
• This includes doing checks that were not
done or could not be done in writing the
grammar.
• Example: Redeclared variables.
How is this done: The Symbol
Table
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Symbol Table
• Symbol Table handles creating scopes and merging the parent's scope with the child's.
• When an item is added to the symbol table, it will handle inserting the item into the current scope. • When you look up an identifier in the symbol
table to see if it is visible, it will handle looking in the current scope and the parent's scope.
February 26, 2002 — CSE 131A — Winter, 2002
Symbol Table Structure
• The Symbol Table is an external data
structure that maintains information about
the identifiers in a program.
• It should not be part of the parser, because
the symbol table is used in all areas of the
compiler.
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
VAR x : INTEGER
• You will (1) store your symbol table such
that x is:
– project 3: a variable with name x
– project 4: ...whose type is INTEGER ... – project 5: ...and is at memory location x.
• So we're only doing part of this, right now.
x := 0
• ...when (2) x is used (above) in the assignment statment, you will look up x in your symbol table to see if it has been declared.
• Then you will ask (3), "Is it a variable?" to see if the assignment is legal. i.e.:
– CONST CheckingBalance = 0; – CheckingBalance := 1;
• ...gives an error, because constants cannot be modified. Next quarter, you will ask if x's type is integer, so that it can store the value.
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Symbol Table Definition
• Definition of Scope: Where locally-defined
variables are visible.
• Oberon Scopes are modules, procedures, or
the main program.
• The lifetime of the oberon scope is only
during the parsing of the scope, or the
execution of the scope.
February 26, 2002 — CSE 131A — Winter, 2002
Symbol Table Example
VAR x /* Program started, so in the main's scope */ MODULE m /* Open scope for module */
PROCEDURE y /* Open scope for procedure */ ...
END y /* Close scope for y */ END m /* Close scope for m */
BEGIN
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Nested Scopes
• In Oberon, scopes can be nested: MODULE M
PROCEDURE P
PROCEDURE Q
• Why is this important? When an identifier is used in Q, you must look in Q's scope, then P's, then M's before stating that the identifier is undeclared. You do not look in main's scope, because you stop at Modules.
Can identifiers be redeclared on
same scope?
• Oberon (and most languages) say no
• Example:
PROCEDURE x VAR x : INTEGER VAR x : INTEGER
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Can identifiers be redeclared in
nested/child scopes?
• Oberon (and most languages) say yes. • Example: MODULE M VAR x; PROCEDURE P TYPE x = INTEGER; VAR y : x; END P x := 3;
February 26, 2002 — CSE 131A — Winter, 2002
Can non-local identifiers be
referenced in a local scope?
• This is often called non-local access.
• Oberon says it depends on the scope
• Example:
VAR x
PROCEDURE P ....
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Oberon Visibility: Procedures
• Any identifier declared above the procedure
is visible to the procedure.
• So when you check the visibility of an
identifier, you search the local scope, and
recursively through the parent's scope.
Oberon Visibilty: Modules
• No identifier declared above the module is visible; only the identifiers declared in the module are visible. The recursion stops when there is no parent. VAR x; MODULE m; VAR y; PROCEDURE z ... BEGIN
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Oberon Visibilty: Modules
• To check the visibility of an identifier, search the local scope, and recurse through the parent scope. The recursion stops if the scope is a module. • One exception to the module rule: identifiers can
be imported into the module from the parent scope to make the identifier visible to the module.
VAR x, y MODULE m
IMPORT x BEGIN
x := 3; /* Ok, since imported, is visible */ y := 2; /* Error, y is not visible in module */
February 26, 2002 — CSE 131A — Winter, 2002
Tackling this Project
• This is a long project and there are a lot of details. • There are 2 1/2 weeks from now in which to finish
the project. Hopefully you have used your
weekend to familiarize yourself with the spec and fix any lingering parser problems.
• Set deadlines for yourself. Get your symbol table working. Start now.
February 26, 2002 — Lecture #15
CSE 131A — Winter, 2002 Sean Peisert
Timeframe for the rest of the
Quarter
• Thursday: More on Symbol Table
• Friday: Extremely important discussion section led by Steven Tapping to clarify remaining Symbol Table Questions.
• Week 9, Tuesday: Abstract Syntax Trees
• Week 9, Thursday: Syntax-Directed Definitions • Week 10, Tuesday: Other topics & diferent
parsers