Syntax and Grammar

Syntax Specification

Syntax in a programming language is specificied using a mathematical device called a Grammar.

Example: A really simple language’s syntax

Arithmetic Expression

AExp \to IntBit | AExp + AExp | ...

Boolean Expression

BExp \to BLit | BExp < BExp | ...

BLit: Boolean Literal

Statements:

\text{Stmnt} \to \text{Assgment} | \text{IFThen | \text{while}

et cetera…

Variables and Binding

Variables

Variable: Store data and associate it with a name.

Variables have two values associated with them.

Example: A variable
int x = 42

Symbol table:

SymbolL-ValueR-Value
xi42
Example: A pointer
int x = 42;
int *p = &x;

Symbol table:

SymbolL-ValueR-Value
xi42
pji

Notice how *p and x become aliases for the same memory location, which can lead to potential problems.

Binding

Binding: Association of a name to a particular property.

\begin{matrix} &\text{Symbol} & & &\text{Property} \\ &x &\to& &p \end{matrix}

Types of Binding:

Binding Time: When the binding happens

  1. Dynamic Binding: If the binding happens while the program is running.
  2. Static Binding: If the binding happens outside program execution.

More on Static Binding: Two types

  1. Compiler does it
  2. Lexer does it.

Note: This has nothing to do with the static keyword in Java.

Memory Binding (Memory Allocation)

Static Allocation: Allocation happens before execution (typically the compiler)

Process Memory Heap:

TODO plantuml

Refresher: Frames store the local variables and parameters of a function call.

Dynamic Allocation: Allocation happens during execution.

Example: Static and dynamic allocation in C.
// Statically allocate n=3
x = int[3];

// Dynamically allocate n=3
y = malloc(sizeof(int) * 3);

In dynamic languages (e.g., Python, Ruby, Perl), all arrays are dynamic, because there is no static allocation in an interpreted language.

Type Checking

Typing: Assigning a type to every symbol in a program, and checking the type assignment makes sense.

Static Typing

Static Typing: Happens before execution, typically during compilation

Example: Explicit and implicit typing in Rust
// Explicit Typing
let x : f64 = 1.0;

// Implicit Typing (type inference)
let y = 1.0;

On Overhead:

Typing adds a lot of overhead, which is why there’s a gap in performance between compiled and dynamic languages.

Dynamic Typing

Dynamic Typing: Happens during execution

Strict Typing: When the type is allowed to change.

Example: Strict typing

Suppose:

x := 42
// ...
x := "Apple"

If this is disallowed, the typing is said to be strict.

Scope Binding

Scope: The region of a program where a symbol is visible.

Remember: This is about visibility, not permanence!

Globals: Symbols which ignore scoping rules and are visible everywhere.

Example: Scope

Typical example:

fn f() {
  let x = 42;
}

fn g() {
  let y = 42;
}

Wacky example:

// We can statically define variables in c
f() {
  static x = 0;
  x++;
}

Static Scoping (Lexical Scoping)

Scoping happens while writing the program.

Dynamic Scoping

Scoping determined at runtime.

Example: Dynamic scoping and shadowing
z := 0
x := 0
call g
call f

function f() {
// Shadows the x outside
  x := 42
  call g
}

function g() {
// Uses the x in the function that called me
  y := x \text{$+$} 7
}

For most dynamic languages, this is an optional thing you have to turn on.

Types

Data Type: Property of symbol that defines its nature (size, structure, etc.)

1. Primitive Types

Primitive: Types that are deined by language

More Accurate: Type that is not made from other types

Typical Primitive Types:

Example: Aside: Typical size of typical primitives.
TypeBytes
bool1
int32
double64
float32
char1—4

2. Non-Primitive Types

Typical Non-Primitive Types:


Now we’ll discuss some data types.


Strings

Most languages implement Strings as:

  1. First-class citizens, and
  2. Internally implements as arrays of characters.

Implementations:

  1. Fixed Size Immutable: Easily implemented with arrays. (Java)
  2. Bounded Dynamic Size: Can also be implemented with array, where the size of the array is the upper bound on the size of the string. (C/C++)
  3. Fully Dynamic: Can be implemented with buffered arrays or linked lists (Ruby, Python)
Example: Implementation of string

Arrays are very fast to read and easy to use, which is why they are behind most String implementations.

Suppose you want to naively concatenate three Strings in Java: (A + B + C).

Arrays

Array: Indexed sequence of elements of the same type.

Why Elements of the Same Type?:

e.g., we know to get arr[5], we just do \text{base address} + 5 \times \text{size of type}

Design Issues

Type of Subscript

Traditionally this is an integer, but some domains may want us to change it.

Range Checking

Is array index bounds checking provided?

Not doing range checking will save some time.

Dynamic Range Checking: Range check at runtime

Static Range Checking: Use a static analyzer that looks at each array access and determines if it’s safe.

Dynamic Size?

Recall — Arrays’ random access is made possible by the fact they are allocated contiguously.

In other words, dynamic array sizes are just abstractions on top of normal fixed-size arrays that simplify the operation for the programmer.

Implemention Methods:

  1. Buffered Array: Allocate extra memory to lower amount of resizes.
  2. Linked List: Totally forgoes speed.

Allocation: Static v. Dynamic

Does the array get allocated statically, dynamically, or in a mixed fashion?

Multidimensional Arrays

Multidimensional Arrays: Indexed by more than one index value.

e.g.,

int[][] arr := new int[3][3];
arr[0][2] := 42;

Implementation Methods:

  1. Array of Arrays: 1D array with pointers to other array(s).
  2. Sequential Allocation: All elements allocated on a single array.

Jagged Array: Multidimensional array in which one of the dimensions is variable.

Example: Implementation methods

Suppose this 2D array:

\begin{bmatrix} &0 &0 &0 \\ &1 &1 &1 \\ &2 &2 &2 \\ \end{bmatrix}

1. Array of Arrays

// Array of pointers
arr = [0x0, 0x1, 0x2]
// Arrays containing data
0x0 = [0, 0, 0]
0x1 = [1, 1, 1]
0x2 = [2, 2, 2]

2. Sequential Allocation

arr = [0,0,0,1,1,1,2,2,2]

// Jump 2 blocks, get element 1
arr[2][1]
Example: Jagged array
arr := new array[3][?]
arr[0] \rightarrow [0, 0, 0, 0]
arr[1] \rightarrow [1, 1]
arr[2] \rightarrow [2, 2, 2]

Associative Arrays

Map keys of any type to values.

grades["John Romero"] = 3

Other Operations


Next section:

Records and Tuples