Syntax in a programming language is specificied using a mathematical device called a Grammar.
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…
Variable: Store data and associate it with a name.
Variables have two values associated with them.
int x = 42Symbol table:
| Symbol | L-Value | R-Value |
|---|---|---|
| x | i | 42 |
| … | … | … |
- Static type checking is an uncomputable problem. There is no algorithm that can, for all possible instances, solve that problem.
int x = 42;
int *p = &x;Symbol table:
| Symbol | L-Value | R-Value |
|---|---|---|
| x | i | 42 |
| p | j | i |
| … | … | … |
Notice how
*pandxbecome aliases for the same memory location, which can lead to potential problems.
Binding: Association of a name to a particular property.
\begin{matrix} &\text{Symbol} & & &\text{Property} \\ &x &\to& &p \end{matrix}
Types of Binding:
- Memory Allocation: Memory binding
- Typing: Type binding
- Scoping: Scope binding
Binding Time: When the binding happens
More on Static Binding: Two types
- Compiler does it
- Lexer does it.
Note: This has nothing to do with the
statickeyword in Java.
Static Allocation: Allocation happens before execution (typically the compiler)
Process Memory Heap:
TODO plantuml
- stack (stack frames)
- heap (dynamically allocated data)
- data (statically allocated data).
- text
Refresher: Frames store the local variables and parameters of a function call.
Dynamic Allocation: Allocation happens during execution.
// 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.
Typing: Assigning a type to every symbol in a program, and checking the type assignment makes sense.
Static Typing: Happens before execution, typically during compilation
// 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.
- In the modern day, with JIT and other optimizations, this gap is shortening.
Dynamic Typing: Happens during execution
Strict Typing: When the type is allowed to change. Suppose: If this is disallowed, the typing is said to be strict.Example: Strict typing
x := 42// ...x := "Apple"
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.
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++;
}Scoping happens while writing the program.
Scoping determined at runtime.
z := 0x := 0call gcall ffunction f() {// Shadows the x outsidex := 42call g}function g() {// Uses the x in the function that called mey := x \text{$+$} 7}
For most dynamic languages, this is an optional thing you have to turn on.
Data Type: Property of symbol that defines its nature (size, structure, etc.)
Primitive: Types that are deined by language
More Accurate: Type that is not made from other types
Typical Primitive Types:
| Type | Bytes |
|---|---|
| bool | 1 |
| int | 32 |
| double | 64 |
| float | 32 |
| char | 1—4 |
Typical Non-Primitive Types:
Now we’ll discuss some data types.
Most languages implement Strings as:
- First-class citizens, and
- Internally implements as arrays of characters.
Implementations:
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).
StringBuffer instead, which allocates a large array to reduce allocations.Array: Indexed sequence of elements of the same type.
Why Elements of the Same Type?:
- Every element must have the same size to achieve random-access.
e.g., we know to get arr[5], we just do \text{base address} + 5 \times \text{size of type}
Traditionally this is an integer, but some domains may want us to change it.
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.
Recall — Arrays’ random access is made possible by the fact they are allocated contiguously.
- Hence, to resize an array, we could allocate a whole new, bigger array and copy the data over.
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:
Does the array get allocated statically, dynamically, or in a mixed fashion?
Multidimensional Arrays: Indexed by more than one index value.
e.g.,
int[][] arr := new int[3][3];
arr[0][2] := 42;Implementation Methods:
Jagged Array: Multidimensional array in which one of the dimensions is variable.
Suppose this 2D array:
\begin{bmatrix} &0 &0 &0 \\ &1 &1 &1 \\ &2 &2 &2 \\ \end{bmatrix}
1. Array of Arrays
// Array of pointersarr = [0x0, 0x1, 0x2]// Arrays containing data0x0 = [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 1arr[2][1]
arr := new array[3][?]arr[0] \rightarrow [0, 0, 0, 0]arr[1] \rightarrow [1, 1]arr[2] \rightarrow [2, 2, 2]
Map keys of any type to values.
grades["John Romero"] = 3
Next section:
Records and Tuples