Presentation is loading. Please wait.

Presentation is loading. Please wait.

Programming Languages

Similar presentations


Presentation on theme: "Programming Languages"— Presentation transcript:

1 Programming Languages
CSCE 314 Programming Languages Grammars and Type Systems Adapted directly from slides by Hyunyoung Lee

2 Types and Type Systems What does this mean? floating point number 3.375? integer ? two short integers and 0? four ASCII X NUL NUL? The way we interpret data is by types Different languages handle types differently, and this is an important distinction. But first, let’s look at grammars a little more…

3 What Is a Programming Language?
Language = syntax + semantics The syntax of a language is concerned with the form of a program: how expressions, commands, declarations etc. are put together to result in the final program. The semantics of a language is concerned with the meaning of a program: how the programs behave when executed on computers Syntax defines the set of valid programs, semantics how valid programs behave

4 Language Syntax Defines legal programs: Defined by grammar rules
programs that can be executed by machine Defined by grammar rules Define how to make “sentences” out of “words” For programming languages Sentences are called statements, expressions, terms, commands, and so on Words are called tokens Grammar rules describe both tokens and statements Often, grammars alone cannot capture exactly the set of valid programs. Grammars combined with additional rules are a common approach.

5 Language Syntax (Cont.)
Statement is a sequence of tokens Token is a sequence of characters Lexical analyzer produces a sequence of tokens from a character sequence Parser produces a statement representation from the token sequence Statements are represented as parse trees (abstract syntax tree)

6 Backus-Naur Form (BNF)
BNF is a common notation to define programming language grammars A BNF grammar G = (N, T, P, S) A set of non-terminal symbols N A set of terminal symbols T (tokens) A set of grammar rules P A start symbol S Grammar rule form (describe context-free grammars): <non-terminal> ::= <sequence of terminals and non-terminals>

7 Examples of BNF BNF rules for robot commands Rules:
A robot arm accepts any command from the set {up, down, left, right} Rules: <move> ::= <command> | <command> <move> <command> ::= up <command> ::= down <command> ::= left <command> ::= right Examples of accepted sequences up down left up up right

8 How to Read Grammar Rules
From left to right Generates the following sequence Each terminal symbol is added to the sequence Each non-terminal is replaced by its definition For each |, pick any of the alternatives Note that a grammar can be used to both generate a statement, and verify that a statement is legal The latter is the task of parsing – find out if a sentence (program) is in a language, and how the grammar generates the sentence

9 Extended BNF Constructs and notation: Example <x> nonterminal x
<x> ::= Body <x> is defined by Body <x> <y> the sequence <x> followed by <y> {<x>} the sequence of zero or more occurrences of <x> {<x>}+ the sequence of one or more occurrences of <x> [<x>] zero or one occurrence of <x> Example <expression> ::= <variable> | <integer> <expression> ::= <expression> + <expression> | ... <statement> ::= if <expression> then <statement> { elseif <expression> then <statement> }+ [ else <statement> ] end | ... <statement> ::= <expression> | return <expression> | ...

10 Example Grammar Rules (Part of C++ Grammar)
selection-statement: if ( condition ) statement if ( condition ) statement else statement switch ( condition ) statement condition: expression type-specifier-seq declarator = assignment-expression iteration-statement: while ( condition ) statement do statement while ( expression ) ; for ( for-init-statement ; conditionopt ; expressionopt ) statement for-init-statement: expression-statement simple-declaration jump-statement: break ; continue ; return expressionopt ; goto identifier ; declaration-statement: block-declaration A.5 Statements statement: labeled-statement expression-statement compound-statement selection-statement iteration-statement jump-statement declaration-statement try-block labeled-statement: identifier : statement case constant-expression : statement default : statement expression-statement: expressionopt ; compound-statement: { statement-seqopt } statement-seq: statement statement-seq statement

11 Context Free Grammars A grammar G = (N, T, S, P) with the set of alphabet V is called context free if and only if all productions in P are of the form A -> B where A is a single nonterminal symbol and B is in V*. The reason this is called “context free” is that the production A -> B can be applied whenever the symbol A occurs in the string, no matter what else is in the string. Example: The grammar G = ( {S}, {a,b}, S, P ) where P = { S -> ab | aSb } is context free. The language generated by G is L(G) = { anbn | n >= 1}.

12 Concrete vs. Abstract Syntax
Concrete syntax tree Result of using a programming language grammar to parse a program is a parse tree Contains every symbol in the input program, and all non-terminals used in the program’s derivation Abstract syntax tree (AST) Many symbols in input text are uninteresting (punctuation, such as commas in parameter lists, etc.) AST only contains “meaningful” information Other simplifications can also be made, e.g., getting rid of syntactic sugar, removing intermediate non-terminals, etc.

13 Ambiguity (1) A grammar is ambiguous if there exists a string which gives rise to more than one parse tree E.g., infix binary operators ‘-’ <expr> ::= <num> | <expr> ‘-’ <expr> Now parse 1 – 2 - 3 As (1-2)-3 As 1-(2-3)

14 Ambiguity (2) E.g., infix binary operators ‘+’ and ‘*’
<expr> ::= <num> | <expr> + <expr> | <expr> * <expr> | <expr> == <expr> Now parse * 3 As (1+2)*3 As 1+(2*3)

15 Resolving Ambiguities
Between two calls to the same binary operator Associativity rules left-associative: a op b op c parsed as (a op b) op c right-associative: a op b op c parsed as a op (b op c) By disambiguating the grammar <expr> ::= <num> | <expr> ‘-’ <expr> vs. <expr> ::= <num> | <expr> ‘-’ <num> Between two calls to different binary operator Precedence rules if op1 has higher-precedence than op2 then a op1 b op2 c => (a op1 b) op2 c if op2 has higher-precedence than op1 then a op1 b op2 c => a op1 (b op2 c)

16 Resolving Ambiguities (Cont.)
Rewriting the ambiguous grammar: <expr> ::= <num> | <expr> + <expr> | <expr> * <expr> | <expr> == <expr> Let us give * the highest precedence, + the next highest, and == the lowest <expr> ::= <sum> { == <sum> } <sum> ::= <term> | <sum> + <term> <term> ::= <num> | <term> * <num>

17 Dangling-Else Ambiguity
Ambiguity in grammar is not a problem occurring only with binary operators For example, <S> ::= if <E> then <S> | if <E> then <S> else <S> Now consider the string: if A then if B then X else Y if A then ( if B then X else Y ) ? if A then ( if B then X ) else Y ?

18 Chomsky Hierarchy Four classes of grammars that define particular classes of languages Regular grammars Context free grammars Context sensitive grammars Phrase-structure (unrestricted) grammars Ordered from less expressive to more expressive (but faster to slower to parse) Regular grammars and CF grammars are of interest in theory of programming languages

19 Regular Grammar Productions are of the form A -> aB or A -> a
where A, B are nonterminal symbols and a is a terminal symbol. Can contain S -> λ. Example regular grammar G = ({A, S}, {a, b, c}, S, P), where P consists of the following productions: S -> aA A -> bA | cA | a G generates the following words aa, aba, aca, abba, abca, acca, abbba, abbca, abcba, … The language L(G) in regular expression: a(b+c)*a

20 Regular Languages The following three formalisms all express the same set of (regular) languages: Regular grammars Regular expressions Finite state automata Not very expressive. For example, the language L = { anbn | n >= 1 } is not regular. Question: Can you relate this language L to parsing programming languages? Answer: balancing parentheses

21 Finite State Automata b a a S A F c
A finite state automaton M=(S, I, f, s0, F) consists of: a finite set S of states a finite set of input alphabet I a transition function f: SxI -> S that assigns to a given current state and input the next state of the automaton an initial state s0, and a subset F of S consisting of accepting (or final) states Example: Regular grammar FSA S -> aA A -> bA | cA | a Regular expression a(b+c)*a b a a S A F c

22 Why a Separate Lexer? Regular languages are not sufficient for expressing the syntax of practical programming languages, so why use them? Simpler (and faster) implementation of the tedious (and potentially slow) “character-by-character” processing: DFA gives a direct implementation strategy Separation of concerns – deal with low level issues (tabs, linebreaks, token positions) in isolation: grammars for parsers need not go below token level

23 Summary of the Productions
Phrase-structure (unrestricted) grammars A -> B where A is a string in V* containing at least one nonterminal symbol, and B is a string in V*. Context sensitive grammars lAr -> lwr where A is a nonterminal symbol, and w a nonempty string in V*. Can contain S ->λ if S does not occur on RHS of any production. Context free grammars A -> B where A is a nonterminal symbol. Regular grammars A -> aB or A -> a where A, B are nonterminal symbols and a is a terminal symbol. Can contain S -> λ.

24 Variables and Type Systems

25 Names Names refer to different kinds of entities in programs, such as variables, functions, classes, templates, modules, Names can be reserved or user-defined Names can be bound statically or dynamically Name bindings have a scope: the program area where they are visible

26 Variables Essentially, variables are bindings of a name to a memory address. They also have a type, value, and lifetime Bindings can be dynamic (occur at run time), or static (occur prior to run time) What are the scopes of names here, when are variables bound to types and values, and what are their lifetimes? const int d = 400; void f() { double d = 100; { double d = 200; std::cout << d;} std::cout << d; } double g() { return d+1;}

27 Scope Scope is a property of a name binding
The scope of a name binding are the parts of a program (collection of statements, declarations, or expressions) that can access that binding Static/lexical scoping Binding’s scope is determined by the lexical structure of the program (and is thus known statically) The norm in most of today’s languages Efficient lookup: memory location of each variable known at compile-time Scopes can be nested – inner bindings hide the outer ones

28 Lexical Scoping namespace std { ... } namespace N { void f(int x) {};
class B { void f (bool b) { if (b) { bool b = false; // confusing but OK std::cout << b; } };

29 Dynamic Scoping Some versions of LISP have dynamic scoping
Variable’s binding is taken from the most recent declaration encountered in the execution path of the program Macro expansion of the C preprocessor gives another example of dynamic scoping Makes reasoning difficult. For example, #define ADD_A(x) x + a void add_one(int *x) { const int a = 1; x = ADD_A(x); } void add_two(int *x) { const int a = 2; x = ADD_A(x); }

30 l- and r-values Depending on the context, a variable can denote the address (l-value), or the value (r-value) int x; x = x + 1; Some languages distinguish between the syntax denoting the value and the address, e.g., in ML x := !x + 1 From type checking perspective, l- or r-valueness is part of the type of an expression

31 Lifetime Time when a variable has memory allocated for it
Scope and lifetime of a variable often go hand in hand A variable can be hidden, but still alive A variable can be in scope, but not alive void f (bool b) { if (b) { bool b = false; // hides the parameter b std::cout << b; } A* a = new A(); A& aref = *a; delete a; std::cout << aref; // aref is not alive, but in scope

32 Variable-Type Binding
Types can be bound to variables statically or dynamically Static: string x = “Hi”; x = 1.2; // error Dynamic: string x = “Hi”; x = 1.2; // OK Static binding may or may not require annotations let x = 5 x = 5.5 – error in x + 1

33 Types and Type Systems Types are collections of values (with operations that can apply to them) At the machine level, values are just sequences of bits Is this floating point number 3.375? integer ? two short integers and 0? four ASCII X NUL NUL? Programming at machine-level (assembly) requires that programmer keeps track of what are the types of each piece of data Type errors (attempting an operation on a data type for which the operation is not defined) hard to avoid Goal of type systems is to enable detection of type errors – reject meaningless programs

34 Languages with some type system, but unsound
C, C++, Eiffel Reject most meaningless programs: int i = 1; char* p = i; but allow some: and deem the behavior undefined – just let the machine run and do whatever union { char* p; int i; } my_union; void foo() { my_union.i = 1; char* p = my_union.p; . . . }

35 Sound Type System: Java, Haskell
Reject some meaningless programs at compile-time: Int i = “Erroneous”; Add checks at run-time so that no program behavior is undefined interface Stack { void push(Object elem); Object pop(); } class MyStack { } Stack s = new MyStack(); s.push(1); s.push(”whoAreYou…”); Int i = (Int) s.pop(); // throws an exception

36 Dynamic (but Sound) Type System
Scheme, Javascript Reject no syntactically correct programs at compile-time, types are enforced at run-time: (car (cons 1 2)) ; ok (car 5) ; error at run-time Straightforward to define the set of safe programs and to detect unsafe ones

37 Type Systems Common errors -- examples of operations that are outlawed by type systems: Add an integer to a function Assign to a constant Call a non-existing function Access a private field Type systems can help: in early error detection in code maintenance in enforcing abstractions in documentation in efficiency

38 Type Systems Terminology
Static vs. dynamic typing Whether type checking is done at compile time or at run time Strong vs. weak typing Sometimes means no type errors at run time vs. possibly type errors at run time (type safety) Sometimes means no coersions vs. coersions (implicit type conversion) Sometimes even means static vs. dynamic

39 Type Systems Terminology (Cont.)
Type inference Whether programmers are required to manually state the types of expressions used in their program or the types can be determined based on how the expr.s are used E.g., C requires that every variable be declared with a type; Haskell infers types based on a global analysis

40 Type Checking in Language Implementation

41 Type Checking Reminder: CF grammars can capture a superset of meaningful programs Type checking makes this set smaller (usually to a subset of meaningful programs) What kind of safety properties can CF grammars not express? Variables are always declared prior to their use Variable declarations unique As CF grammars cannot tie a variable to its definition, must parse expressions “untyped,” and type-check later Type checker ascribes a type to each expression in a program, and checks that each expression and declaration is well-formed

42 Typing Relation By “expression t is of type T”, it means that we can see (without having to evaluate t) that when t is evaluated, the result is some value t’ of type T All of the following mean the same “t is of type T”, “t has type T”, “type of t is T”, “t belongs to type T” Notation: t : T or t ∈ T or t :: T (in Haskell) more commonly, Γ ⊢ t : T where Γ is the context, or typing environment What are the types of expression x+y below? float f(float x, float y) { return x+y; } int g(int x, int y) { return x+y; } x : float, y : float ⊢ x+y : float x : int, y : int ⊢ x+y : int

43 Type Checker As a Function
Type checker is a function that takes a program as its input (as an AST) and returns true or false, or a new AST, where each sub-expression is annotated with a type, function overloads resolved, etc. Examples of different forms of type checking functions: checkStmt :: Env -> Stmt -> ( Bool, Env ) checkExpr :: Env -> Expr -> Type

44 Defining a Type System Informal rules in some natural language
Using some formal language Implementation

45 Defining a Type System with Informal Rules – Example Type Rules
All referenced variables must be declared All declared variables must have unique names The + operation must be called with two expressions of type int, and the resulting type is int

46 Defining a Type System with Informal Rules – Example Type Check Statement
Skip is always well-formed An assignment is well-formed if its target variable is declared, its source expression is well-formed, and the declared type of the target variable is the same as the type of the source expression A conditional is well-formed if its test expression has type bool, and both then and else branches are well-formed statements

47 Defining a Type System with Informal Rules – Example Type Check Statement (Cont.)
A while loop is well-formed if its test expression has type bool, and its body is a well-formed statement A block is well-formed if all of its statements are well-formed A variable declaration is well-formed if the variable has not already been defined in the same scope, and if the type of the initializer expression is the same as the type of the variable

48 Defining a Type System Using Formal Language
Common way to specify type systems is using natural deduction style rules – “inference rules” A An B Example: A ∧ B A => B A B B (Do they look/sound familiar?)

49 Type Rules – Example A conditional is well-formed if its test expression has type bool, and both then and else branches are well-formed statements Γ ⊢ e : bool Γ ⊢ s1 : ok Γ ⊢ s2 : ok Γ ⊢ if e s1 s2 : ok


Download ppt "Programming Languages"

Similar presentations


Ads by Google