Semantic Analysis - Leonidas Fegaras

advertisement
Semantic Analysis
Leonidas Fegaras
CSE 5317/4305
L6: Semantic Analysis
1
Type Checking
source
file
get next
character
scanner
get
token
parser
AST
type checking
AST
token
symbol
table
type errors
• Checking whether the use of names is consistent with their
declaration in the program
int x;
x := x+1;
x.A := 1;
x[0] := 0;
correct use of x
type errors
• Statically typed languages: done at compile time, not at run time
• Need to remember declarations
– Symbol Table
CSE 5317/4305
L6: Semantic Analysis
2
Symbol Table
• A compile-time data structure used to map names into
declarations
• It stores:
– for each type name, its type definition
• eg. for the C type declaration typedef int* mytype, it maps the name
mytype to a data structure that represents the type int*
– for each variable name, its type
• if the variable is an array, it also stores dimension information
• it may also store storage class, offset in activation record, etc
– for each constant name, its type and value
– for each function and procedure, its formal parameter list and its output
type
• each formal parameter must have
– name
– type
– type of passing (by-reference, by-value, etc)
CSE 5317/4305
L6: Semantic Analysis
3
Symbol Table (cont.)
• Need to capture nested scopes, if necessary
{
int a;
{
int a;
a = 1;
};
a = 2;
};
• Interface:
insert ( key: String, binding: Declaration )
lookup ( key: String ): Option[Declaration]
begin_scope ()
end_scope ()
CSE 5317/4305
L6: Semantic Analysis
4
The Gen Symbol Table
class SymbolTable {
var symbol_table: List[List[(String,Declaration)]] = Nil
def lookup ( key: String ): Option[Declaration] = {
val ds = for ( s <- symbol_table;
(n,d) <- s if n.equals(key)
) yield d
ds match {
case c::cs => Some(c)
case _ => None
}
}
def insert ( key: String, declaration: Declaration ) {
symbol_table match {
case c::cs => symbol_table = ((key,declaration)::c)::cs
case _ => throw new Error("Empty scope")
}
}
CSE 5317/4305
L6: Semantic Analysis
5
The Gen Symbol Table (cont.)
def begin_scope () {
symbol_table = List()::symbol_table
}
def end_scope () {
symbol_table match {
case c::cs => symbol_table = cs
case _ => throw new Error("Empty scope")
}
}
CSE 5317/4305
L6: Semantic Analysis
6
Example
{
int a;
{
int a;
a = 1;
};
a = 2;
};
CSE 5317/4305
L6: Semantic Analysis
7
Type ASTs
• A typechecker is a function that maps an AST that represents an
expression into its type
• Need to define the data structures for types:
sealed abstract class Type
case class IntegerType () extends Type
case class BooleanType () extends Type
case class NamedType ( typename: String ) extends Type
case class ArrayType ( elements: Type ) extends Type
case class RecordType ( components: List[(String,Type)] ) extends Type
CSE 5317/4305
L6: Semantic Analysis
8
Declarations
• The symbol table must contain type declarations (ie.
typedefs),variable declarations, constant declarations, and
function signatures:
var symbol_table: List[List[(String,Declaration)]] = Nil
sealed abstract class Declaration
case class TypeDeclaration ( declaration: Type ) extends Declaration
case class VarDeclaration ( declaration: Type ) extends Declaration
case class ConstantDeclaration ( declaration: Type, value: Exp )
extends Declaration
case class FunctionDeclaration ( result: Type, parameters: List[Type] )
extends Declaration
CSE 5317/4305
L6: Semantic Analysis
9
Typechecking
• A tree traversals that checks each node of the AST tree recursively:
def typecheck ( e: Expr ): Type =
e match {
case IntegerExp => IntegerType
case TrueExp => BooleanType
case FalseExp => BooleanType
case VariableExp(name)
=> st.lookup(name) match {
case Some(VarDeclaration(type) => type
case Some(_) => throw new Error(name+" is not a variable")
case None => throw new Error("Undefined variable: "+name)
}
CSE 5317/4305
L6: Semantic Analysis
10
Typechecking: BinaryExp
case BinOpExp(op,left,right) => {
val left_type = typecheck(left)
val right_type = typecheck(right)
op match {
case "+": if (left_type == right_type && left_type == IntegerType())
left_type
else throw new Error("expected integers in addition")
…
}
CSE 5317/4305
L6: Semantic Analysis
11
Typechecking: CallExp
case CallExp(f,args)
=> st.lookup(f) match {
case Some(FunctionDeclaration(otp,params)) => {
if (params.length != args.length)
throw new Error("Number of parameters doesn't match number of arguments")
else (args.map(typecheck(_)) zip params).map({
case (atp,(_,ptp))
=> if (!equal_types(atp,ptp))
throw new Error("The type of call argument ("+atp
+") does not match the type of the formal parameter: "+ptp)
})
otp
}
case _ => throw new Error("Undefined function: "+f) }
• equal_types(x,y) checks the types x and y for equality
• Two types of type equality: type equality based on type name
equivalence, or based on structural equivalence
CSE 5317/4305
L6: Semantic Analysis
12
The Calculator Interpreter
• Evaluate an expression e using a symbol table st:
def eval ( e: Expr ): Double = {
e match {
case RealConst(v) => v
case IntConst(v) => v
case StringConst(v) => throw new Error("Strings are not permitted: "+e)
case Var(v)
=> st.lookup(v) match {
case Some(VarDec(v)) => v
case Some(_) => throw new Error(v+" is not a variable")
case None => throw new Error("Undefined variable: "+v)
}
case IfExp(e1,e2,e3)
=> if (eval(e1) > 0.0)
eval(e2)
else eval(e3)
CSE 5317/4305
L6: Semantic Analysis
13
The Calculator Interpreter (cont.)
case CallExp(fnc,args)
=> st.lookup(fnc) match {
case Some(FunDec(body,params))
=> if (params.length != args.length)
throw new Error("Number of parameters does not much number of arguments")
else {
st.begin_scope()
(args.map(eval(_)) zip params).map({ case (a,p) => st.insert(p,new VarDec(a)) })
val res = eval(body)
st.end_scope()
res
}
case Some(_) => throw new Error(fnc+" has not been defined as a function");
case None => throw new Error("Undefined function: "+fnc)
}
CSE 5317/4305
L6: Semantic Analysis
14
The Calculator Interpreter (cont.)
case BinOpExp(op,e1,e2)
=> { val left = eval(e1)
val right = eval(e2)
op match {
case "plus" => left + right
case "minus" => left - right
case "times" => left * right
case "div" => left / right
case "and" => if ((left > 0.0) && (right > 0.)) 1.0 else 0.0
case "or" => if ((left > 0.0) || (right > 0.0)) 1.0 else 0.0
case "eq" => if (left == right) 1.0 else 0.0
case "ne" => if (left != right) 1.0 else 0.0
case "gt" => if (left > right) 1.0 else 0.0
case "lt" => if (left < right) 1.0 else 0.0
case "ge" => if (left >= right) 1.0 else 0.0
case "le" => if (left <= right) 1.0 else 0.0
case _ => throw new Error("Unrecognized binary operation: "+e)
}
}
case _ => throw new Error("Unrecognized expression: "+e)
CSE 5317/4305
L6: Semantic Analysis
15
Download