0% found this document useful (0 votes)
5 views

V02 Parsing, Conditionals, Names

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

V02 Parsing, Conditionals, Names

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 64

Concepts of Programming Languages

PARSING, CONDITIONALS, NAMES

Prof. Dr. Mira Mezini

1
COPL

PARSING
How to go from concrete syntax to abstract syntax?

2
Parsing
Concrete syntax definition
— defines program text; recipe for
parsing and disambiguation
Program in concrete
syntax
In general, the process
of converting the
parsing + concrete syntax into
disambiguation ASTs is called parsing

Abstract Syntax Tree


Abstract syntax
representation
— defines program
structure; no irrelevant
3details
Parsing & Disambiguation by Example

4+5*6

+ *
4 * or + 6
5 6 4 5
(4 + (5 * 6)) ((4 + 5) * 6)

4
Parsing — A Subject of Study on its Own

Parser generators Parser combinators in


SDF ANTLR yacc Haskell Scala Python

Bison SableCC Happy …


Parsing algorithms
GLR SGLR LALR(1)
LL(k) LR(1) Early

5
No parsing in the rest of this course

We avoid the topic of parsing altogether.

We rely on very simple concrete syntax


• Scala AST representation
• S-Expressions

6
S-expressions

4+5*6 ⇒ (+ 4 (* 5 6))

An S-expression is either:
enum SExp:
• A literal case SList(list: List[SExp])
• Symbol, Number, String case SSym(symbol: String)
• A list of s-expressions case SNum(num: Int)
• In parenthesis, separated by spaces case SString(string: String)

7
S-expressions

Scala AST S-Expr

Num(1) ⇒ 1

Add(
Num(1) ⇒ (+ 1 2)
Num(2))

Add(
Num(1)
Add( ⇒ (+ 1 (+ 2 3))
Num(2)
Num(3))

8
Concrete Syntax as s-expressions

We define valid expressions using Backus-Naur-Form grammars

Exp ::= Int (Num)


| (+ Exp Exp) (Plus)
| (* Exp Exp) (Mult)

enum Exp:
case Num(n: Int)
case Plus(l: Exp, r: Exp)
case Mult(l: Exp, r: Exp)

10
S-Expression Parsing

SList(List(
SSym("+"),
Plus(
SNum(23),
Num(23),
(+ 23
(* 5 6))
⇒ SList(List(
SSym("*"),
⇒ Mult(
Num(5),
SNum(5),
Num(6)))
SNum(6)))))

Concrete S-Expression Abstract


Syntax Syntax Syntax

11
Lisp

John McCarthy (Sep. 4, 1927 – Oct.24, 2011) was an American


computer scientist and cognitive scientist. He coined the term "artificial
intelligence" (AI), developed the Lisp programming language family,
significantly influenced the design of the ALGOL programming language,
popularized timesharing, and was very influential in the early development
of AI. McCarthy received many accolades and honors, such as the Turing
Award for his contributions to the topic of AI, the United States National
Medal of Science, and the Kyoto Prize.

Around 1959, he invented so-called "garbage


collection" methods to solve problems in Lisp.
Based on the lambda calculus, Lisp soon
became the programming language of choice for
AI applications after its publication in 1960.

12 source: https://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)
Syntax versus Semantics

Semantics

Abstract
Syntax parse desugar Core interpret Value
Syntax

Syntax

13
COPL

CONDITIONALS
There can be very complex conditional expressions in real languages.
For now we’ll keep it simple and will only consider a simple if-then-else.

14
How Proceed to Add Conditionals

How would you proceed to


add conditionals?

15
How Proceed to Add Conditionals

1. Extend the datatype representing expressions to include conditionals

2. Extend the interpreter to handle (the representation of) conditional


expressions

3. Extend the parser if we have one to produce the new representations

16
1. Extending the Abstract Representation

17
2. Extending the Interpreter…
What is the meaning of the following?
if (e) { e1 } else { e2 }

what‘s the
result of
interp(c)?

Even this simple construct results in several, mostly


independent design decisions.
What are they?
18
The Design Space of Conditionals
1. What kind of terms can the test-phrase be?
2. What kind of program phrases (terms) are the branches?
Expressions or statements?
3. If branches are expressions and evaluate to values, how
do their values relate? Are they of the same type?

What examples do you know?

19
The Test-Expression

In some languages the test expression must evaluate to Boolean


values (one representing truth and the other falsehood).

Other languages use (several) truthy/falsy values

20
2. Extending the Interpreter
What about this?

So, we use 0 to represent true and anything else is considered false …

We have implemented a kind of if0 construct, which is fine given that


our language has only Ints.

21
2. Extending the Interpreter

So, we have numbers and conditionals …

But one could say that all we’ve done is (mostly) deferring to
Scala to handle these.

Do you (dis)agree?

22
Let’s Add Proper Boolean Expressions

What do we need to do?

23
Let’s Add Proper Boolean Expressions

Well, we have a recipe …


boolean
1. Extend the datatype representing expressions to include conditional
expressions
boolean
1.
2. Extend the interpreter to handle (the representation of) conditional
conditional
expressions

1.
3. Extend the parser if we have one to produce the new representations

Do you think applying the recipe blindly will work?


If no, why not?

24
Extending the Program Representation

Adding Boolean terms is easy …

Num and Bool constructors are capturing syntactic constants in the


program. This is abstract syntax: we are (abstractly) representing the
program that the user wrote, not the result of its evaluation.

They do not represent compound expressions that will evaluate to


numbers or Booleans. What an expression will evaluate to, for now, can
only be determined by running it. Are there other ways?
25
Extending the Interpreter …

… Like This?

26
Extending the Interpreter …

… Like This?

Found: Bool
Required: Int

Found: Int
Required: Bool

27
Design Decision: The Range of Values

• We aren’t interested only in arithmetic expressions; we want to build


full-fledged programming languages.

• They have a wide range of values (i.e., answers): numbers, Boolean,


strings, images, functions, and more…

Thinking about the range of supported values is


another aspect of language design.

28
Implementing the Decision About Values
We first define a datatype that reflects the different kinds of values that
an interpreter can produce. Convention: call the return type constructors
...V to distinguish from the inputs of the interpreter

???

29
Implementing the Decision About Values

We are make a decision here: We are explicitly saying in the interpreter


30
code that we only allow to add numbers.
Encapsulating Design Decision Points

31
Encapsulating Design Decision Points

Build an abstraction:
• encapsulate the decision about what can change, e.g., what
values can be added, in one place for facilitating
experimentation with language design
• keep what is a fixed decision in the interpreter body

Other abstraction points are possible!

32
Encapsulating Design Decision Points

1. Why apply the


NumV constructor
in addDec rather
than in interp?

1. Why not pass


interp of t and
e in the Cond-
branches to
boolDec?

33
Encapsulating Design Decision Points

1. Why apply the


NumV constructor
in addDec rather
than in interp?

1. Why not pass


interp of t and
e in the Cond-
branches to
boolDec?

1. What can be added may change, e.g., to add strings.


2. This would evaluate both of them, which is exactly what a conditional is
supposed to avoid. A fixed decision – we encode it in the interpreter.

34
COPL

NAMES

35
Motivational Example 1

2 * (355 / 113) * 7 Can you explain this code?

val pi = 355 / 113


val radius = 7
val circumference = 2 * pi * radius

Names or identifiers serve as documentation


(Note: names are not variables – variables allow mutation)

36
Motivational Example 2

2 * (355 / 113) * 7 + 2 * (355 / 113) * 9

Can you explain this code?

// computes circumference of circle with radius 7 and 9

val pi = 355 / 113


val circum1 = 2 * pi * 7
val circum2 = 2 * pi * 9
circum1 + circum2

Names help avoid redundancy/repetions

37
Declarations and References

val pi = 355 / 113


val radius = 7
2 * pi * radius In Scala
declaration
reference

let pi = 355 / 113 in Our lang


let radius = 7 in
2 * pi * radius

declaration
reference

38
Binding, Bound, Free Occurrences

let pi = 355 / 113 in


binding
let radius = 7 in
occurrences
2 * pi * radius + offset

bound free
occurrences occurrence

A name x is free in an expression e if x is not bound within e.

x is free in (x + x)
let x = 5 in x + x
x is bound in (let x = 5 in x + x)
39
Let’s Extend the Language with Let and Ids

Quiz: What steps are needed?

40
Concrete Syntax for the Language with Identifiers

Exp ::= Double (Num)


| (+ Exp Exp) (Plus)
| (* Exp Exp) (Mult)
| (/ Exp Exp) (Div)
| (let Symbol Exp Exp) (Let)
| Symbol (Id)

Example program:

val x = 3 + 4 (let x (+ 3 4)
x * x (* x x))

41
Concrete s-expr Syntax

Exp ::= Double (Num)


| (+ Exp Exp) (Plus)
| (* Exp Exp) (Mult)
| (/ Exp Exp) (Div)
| (let Symbol Exp Exp) (Let)
| Symbol (Id)

Task: Translate the program in our s-expr language

val pi = 355 / 113 (let pi (/ 355 113)


val radius = 7 (let radius 7
2 * pi * radius (* 2 (* pi radius))))

42
Abstract Syntax

Exp ::= Double (Num)


| (+ Exp Exp) (Plus)
| (* Exp Exp) (Mult)
| (/ Exp Exp) (Div)
| (let Symbol Exp Exp) (Let)
| Symbol (Id)

43
Semantics of Let

… by the concept of substitution

44
Defining Substitution
Wanted: A definition of the process of substitution
Here is one: Definition (Substitution):
To substitute identifier i in e with expression v, replace
all identifier sub-expressions of e named i with v.

What do you think?

45
Defining Substitution
Wanted: A definition of the process of substitution
Here is one: Definition (Substitution):
To substitute identifier i in e with expression v, replace
all identifier sub-expressions of e named i with v.

Try it out with the following expressions:

let x = 5 in x + x

let x = 5 in
x * (let x = 3 in x)

Our substitution converted a reasonable program into one that would


be rejected by a parser, because it contains 5 where we expect an
identifier. Obviously, the substitution algorithm is too naive.
46
Defining Substitution

Binding Instance: A binding instance of an identifier is the


occurrence of the identifier that gives it its value. In our case, the
<id> position of a ‘let’ is the only binding instance.

Scope: The scope of a binding instance, BI, is the program region, in


which instances of the identifier refer to the value bound by BI.

Bound Instance: An identifier is bound if it is contained within the


scope of a binding instance of its name.

Free Instance: An identifier not contained in the scope of any binding


instance of its name is said to be free.

47
Defining Substitution

let x = 5 in
x + (let x = 3 in
x + x)
What‘s wrong here?

➔ 5 + (let x = 3 in We do not respect scoping!


5 + 5)

➔ 5 + (5 + 5)

➔ 15

48
Defining Substitution 2

Substitution: To substitute identifier i in e with expression v,


replace all free instances of i in e with v.

• This definition implicitly uses a notion of scope


− Substitute only in the scope of the binding instance of identifier

• An inner binding for the same name introduces a new scope.


− The scope of the outer binding is shadowed or masked by the inner
binding.

• Substituting the inner x is wrong.

49
Implementing Substitution

How to handle Id(y)?

50
Implementing Substitution

If y in Id(y) is id, replace Id(y) with the


expression bound to id (bound); otherwise
How to handle Id(y)? return the expression Id(y)unchanged.

51
Implementing Substitution

How to handle Let?

52
Implementing Substitution

How to handle Let?

subst(x, (* 3 4), (let y (+ x 1) (/ x y)))


= (let y (+ (* 3 4) 1) (/ (* 3 4) y))

subst(y, (+ (* 3 4) 1), (/ (* 3 4) y))


= (/ (* 3 4) (+ (* 3 4) 1))
53
Implementing Substitution

What about this?

subst(x, (* 3 4), (let y (+ x 1) (/ x y)))


= (let y (+ (* 3 4) 1) (/ (* 3 4) y))

subst(y, (+ (* 3 4) 1), (/ (* 3 4) y))


= (/ (* 3 4) (+ (* 3 4) 1))

54
Substitution: Corner Case 1

Let’s test with this?


(let x (* 3 4) (let x (+ x 1) (/ x x))

Our current implementation:


(let x (* 3 4) (let x (+ x 1) (/ x x))
=> (let x (+ (* 3 4) 1) (/ (* 3 4) (* 3 4)))
=> (/ (* 3 4) (* 3 4))

Remember: Variable shadowing!


The inner declaration of x shadows the outer declaration of x
55
Substitution: Take 2

We don’t substitute in the body


of inner let, if it binds the
identifier of the outer let.

(let x (* 3 4) (let x (+ x 1) (/ x x))


=> (let x (+ (* 3 4) 1) (/ x x))
=> (/ (+ (* 3 4) 1) (+ (* 3 4) 1))

56
Substitution: Corner Case 2

Try this test now …


(let x y (let y 5 (/ x y))

Our current implementation:


(let x y (let y 5 (/ x y)) Problem: Variable capture.
=> (let y 5 (/ y y)) The binding instance of y in the
=> (/ 5 5) inner let accidentially captures the
free variable y of the outer let.

57
Substitution: Take 3
def subst(id: String, bound: Exp, body: Exp): Exp = body match

case Let(letId, boundE, bodyE) =>
if (id == letId) Let(y, subst(id, bound, boundE), bodyE)
else
if ( !freeVars(bound).contains(letId) )
Let(letId, subst(id, bound, boundE), subst(id, bound, bodyE))
else
val z = freshName(freeVars(bound) ++ freeVars(bodyE))
Let(z, subst(id, bound, boundE), subst(id, bound, subst(letId, Id(z), bodyE))
)
id = x,
bound = y,
body = (let y 5 (\ x y))
(let x y (let y 5 (/ x y))
=>(let x y (let z 5 (/ x z))
letId = y,
=>(let z 5 (/ y z))
boundE = 5,
=>(/ y 5)
58 bodyE = (\ x y)
Capture-Avoiding Substitution

We have defined a capture-avoiding substitution function


that implements sound variable replacement.

This is fundamental for programming languages


and mathematics in general.

59
Alpha-Equivalence

Let(y, bound, body)

Let(z, bound, subst(y, Id(z), body)


if !freeVars(body).contains(z)
60

Alpha-equivalence:
Two terms are identical up to consistent renaming of variables.
• terms have the same structure
• references resolve to the same declarations
• but names used for references and declarations may differ

60
Interpreting Ids

subst(x, (* 3 4), (+ x y)) = (+ (* 3 4) y)

61
Semantics of Id
Any Id in the scope of a let-expr is replaced when the interpreter
encounters that identifier’s binding instance.
− subst replaces identifiers before the interpreter ever “sees”
them.
− the interpreter can’t replace a free name - it halts with an error

62
Two Substitution Regimes
Eager substitution (static1 {let {x {+ 5 5}} {let {y {- x 3}} {+ y y}}}
and dynamic reduction): = {let {x 10} {let {y {- x 3}} {+ y y}}}
= {let {y {- 10 3}} {+ y y}}
avoids re-computing the = {let {y 7} {+ y y}}
same value several times. = {+ 7 7}
= 14

Lazy substitution 2
{let {x {+ 5 5}} {let {y {- x 3}} {+ y y}}}
(Static reduction): the = {let {y {- {+ 5 5} 3}} {+ y y}}
expression may be = {+ {- {+ 5 5} 3} {- {+ 5 5} 3}}
= {+ {- 10 3} {- {+ 5 5} 3}}
evaluated multiple times. = {+ {- 10 3} {- 10 3}}
= {+ 7 {- 10 3}}
= {+ 7 7}
= 14

63
Two Substitution Regimes: Questions

1.Which one have we implemented?

Home work

1.Our example suggests that the eager regime generates an answer in


fewer steps. Is this always true?

{let {x {+ 5 5}}
{let {y 4} {+ y y}}}

2.Do the two regimes always produce the same result?

{let {x {+ z 4}}
{let {y 4} {+ y y}}}

64
Notes on the Practicality of Substitution

Substitution is not practical for implementing


programming languages. Any idea why?

Because our interpreter requires a full traversal of


the body for each variable definition.

(let x (* 3 4) (let y (+ x 1) (let z 12 (/ y z)))

=> (let y (+ (* 3 4) 1) (let z 12 (/ y z))

=> (let z 12 (/ (+ (* 3 4) 1) z))

=> (/ (+ (* 3 4) 1) 12)

65

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy