Importance of Studying Programming Languages
Importance of Studying Programming Languages
Programming Domains
Scientific applications
– Large number of floating point computations
– Fortran
Business applications
– Produce reports, use decimal numbers and characters
– COBOL
Artificial intelligence
– Symbols rather than numbers manipulated
– LISP
Systems programming
– Need efficiency because of continuous use
– C
Web Software
– Eclectic collection of languages: markup (e.g., XHTML), scripting (e.g.,
PHP), general-purpose (e.g., Java)
Language Evaluation Criteria –
Readability : the ease with which programs can be read and understood
Writability : the ease with which a language can be used to create programs
Reliability : conformance to specifications (i.e., performs to its specifications)
Cost : the ultimate total cost
Readability
Overall simplicity
– A manageable set of features and constructs
– Few feature multiplicity (means of doing the same operation)
– Minimal operator overloading
Orthogonality
– A relatively small set of primitive constructs can be combined in a relatively
small number of ways
– Every possible combination is legal
Control statements
– The presence of well-known control structures (e.g., while statement)
Data types and structures
– The presence of adequate facilities for defining data structures
Syntax considerations
– Identifier forms: flexible composition
– Special words and methods of forming compound statements
– Form and meaning: self-descriptive constructs, meaningful keywords
Writability
Simplicity and Orthogonality
– Few constructs, a small number of primitives, a small set of rules for
combining them
Support for abstraction
– The ability to define and use complex structures or operations in ways that allow
details to be ignored
Expressivity
– A set of relatively convenient ways of specifying operations
– Example: the inclusion of for statement in many modern languages
Reliability
Type checking
– Testing for type errors
Exception handling
– Intercept run-time errors and take corrective measures
Aliasing
– Presence of two or more distinct referencing methods for the same memory
location
Readability and writability
– A language that does not support “natural” ways of expressing an algorithm will
necessarily use “unnatural” approaches, and hence reduced reliability
Cost
Training programmers to use language
Writing programs (closeness to particular applications)
Compiling programs
Executing programs
Language implementation system: availability of free compilers
Reliability: poor reliability leads to high costs
Maintaining programs
Others
Portability
– The ease with which programs can be moved from one implementation to
another
Generality
– The applicability to a wide range of applications
Well-definedness
– The completeness and precision of the language‘s official definition
Influences on Language Design
Computer Architecture
– Languages are developed around the prevalent computer architecture, known as
the von Neumann architecture
Programming Methodologies
– New software development methodologies (e.g., object-oriented software
development) led to new programming paradigms and by extension, new
programming languages
Computer Architecture
Well-known computer architecture: Von Neumann
Imperative languages, most dominant, because of von Neumann computers
– Data and programs stored in memory
– Memory is separate from CPU
– Instructions and data are piped from memory to CPU
– Basis for imperative languages
Variables model memory cells
Assignment statements model piping
Iteration is efficient
Programming Methodologies
1950s and early 1960s: Simple applications; worry about machine efficiency
Late 1960s: People efficiency became important; readability, better control
structures
– structured programming
– top-down design and step-wise refinement
Late 1970s: Process-oriented to data-oriented
– data abstraction
Middle 1980s: Object-oriented programming
– Data abstraction + inheritance + polymorphism
Language Categories –
Imperative
– Central features are variables, assignment statements, and iteration
– Examples: C, Pascal
Functional
– Main means of making computations is by applying functions to given
parameters
– Examples: LISP, Scheme
Logic
– Rule-based (rules are specified in no particular order)
– Example: Prolog
Object-oriented
– Data abstraction, inheritance, late binding
– Examples: Java, C++
Markup
– New; not a programming per se, but used to specify the layout of information in
Web documents
– Examples: XHTML, XML
Implementation Methods -
Compilation
– Programs are translated into machine language
Pure Interpretation
– Programs are interpreted by another program known as an interpreter
Hybrid Implementation Systems
– A compromise between compilers and pure interpreters
Compilation
Translate high-level program (source language) into machine code (machine
language)
Slow translation, fast execution
Compilation process has several phases:
– lexical analysis: converts characters in the source program into lexical units
– syntax analysis: transforms lexical units into parse trees which represent the
syntactic structure of program
– Semantics analysis: generate intermediate code
– code generation: machine code is generated
Figure 1.2 Layered View of Computer: The
operating system and language
implementation are layered over Machine Figure 1.3 The Compilation Process
interface of a computer
called Ja
Preprocessors -
Preprocessor macros (instructions) are commonly used to specify
that code from another file is to be included
A preprocessor processes a program immediately before the program
is compiled to expand embedded preprocessor macros
A well-known example: C preprocessor
– expands #include, #define, and similar macros
Compiler
The compiler is software that converts a program written in a high-level language
(Source Language) to a low-level language (Object/Target/Machine Language/0,
1’s).
Phases of Compiler
1. Lexical Analysis: The first stage of compiler design is lexical analysis, also
known as scanning. In this stage, the compiler reads the source code character
by character and breaks it down into a series of tokens, such as keywords,
identifiers, and operators. These tokens are then passed on to the next stage of
the compilation process.
2. Syntax Analysis: The second stage of compiler design is syntax analysis, also
known as parsing. In this stage, the compiler checks the syntax of the source
code to ensure that it conforms to the rules of the programming language. The
compiler builds a parse tree, which is a hierarchical representation of the
program’s structure, and uses it to check for syntax errors.
3. Semantic Analysis: The third stage of compiler design is semantic analysis. In
this stage, the compiler checks the meaning of the source code to ensure that it
makes sense. The compiler performs type checking, which ensures that variables
are used correctly and that operations are performed on compatible data types.
The compiler also checks for other semantic errors, such as undeclared variables
and incorrect function calls.
4. Code Generation: The fourth stage of compiler design is code generation. In
this stage, the compiler translates the parse tree into machine code that can be
executed by the computer. The code generated by the compiler must be efficient
and optimized for the target platform.
5. Optimization: The final stage of compiler design is optimization. In this stage,
the compiler analyzes the generated code and makes optimizations to improve its
performance. The compiler may perform optimizations such as constant folding,
loop unrolling, and function inlining.
The following code is syntactically correct but semantically wrong because it's not
possible to reassign something to a constant variable:
name = "Akash";
The following is syntactically incorrect and thus does not even have any chance to be
semantically correct.
You can think of symbols as the building blocks of grammar. There are two kinds of
symbols:
Terminal (or Terminal symbol): Terminals are strings written within quotes. They
are meant to be used as they are. Nothing is hidden behind them. For
example "freeCodeCamp" or "firefly".
Non-terminal (or Non-terminal symbol): Sometimes we need a name to refer to
something else. These are called non-terminals. In BNF, non-terminal names are
written within angle brackets (for example <statement>), while in EBNF they don't
usually use brackets (for example statement).
What is BNF?
BNF stands for Backus–Naur Form which resulted primarily from the contributions
of John Backus and Peter Naur.
Left-hand side: Here we write a non-terminal to define it. In the above example, it
is <something>.
::=: This character group separates the Left hand side from Right hand side. Read
this symbol as "is defined as".
Right-hand side: The definition of the non-terminal specified on the right-hand side.
In the above example, it's "content".
The above <something> is just one thing fixed thing. Let's now see all the ways you
can compose a non-terminal.
Sequencing
Choice
You can just write a combination of one or more terminals or non-terminals in a
sequence and the result is their concatenation, with non-terminals being replaced by
their content. For example, you can express your breakfast in the following ways:
It means the only option for breakfast for you is "tea and biscuit". Note that here, the
order of symbols is important.
What is EBNF?
digits = digit { digit }digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
The braces above mean that its inner part may be repeated 0 or more times. It frees
your mind from getting lost in recursion.
One interesting fact is that everything you can express in EBNF can also be expressed
in BNF.
EBNF usually uses a slightly different notation than BNF. For example:
Don't assume that these styles to be universal. There are several variants of them and
they are usually clear from the context. The more important thing to focus on is the
new operations it offers like the braces we've seen above.
Option
Repetition
Grouping
Option
Option uses square brackets to make the inner content optional. Example:
Repetition
Curly braces indicate the inner content may be repeated 0 or more times. You have
already seen a good example of it above. Below is a very simple one just to make the
idea solid in your mind:
Grouping
Parentheses can be used to indicate grouping. It means everything they wrap can be
replaced with any of the valid strings that the contents of the group represent
according to the rules of EBNF. For example:
With BNF we could not do that in one line. It would look like the following in BNF:
<fly> ::= <type> "fly"
Context free grammar is a formal grammar which is used to generate all possible
strings in a given formal language.
1. G= (V, T, P, S)
Where,
Production rules:
1. S → aSa
2. S → bSb
3. S → c
Now check that abbcbba string can be derived from the given CFG.
1. S ⇒ aSa
2. S ⇒ abSba
3. S ⇒ abbSbba
4. S ⇒ abbcbba
By applying the production S → aSa, S → bSb recursively and finally applying the
production S → c, we get the string abbcbba.
Derivation
Derivation is a sequence of production rules. It is used to get the input string through
these production rules. During parsing we have to take two decisions. These are as
follows:
Left-most Derivation
In the left most derivation, the input is scanned and replaced with the production rule
from left to right. So in left most derivatives we read the input string from left to right.
Example:
Production rules:
1. S = S + S
2. S = S - S
3. S = a | b |c
Input:
a-b+c
1. S = S + S
2. S = S - S + S
3. S = a - S + S
4. S = a - b + S
5. S = a - b + c
Right-most Derivation
In the right most derivation, the input is scanned and replaced with the production rule
from right to left. So in right most derivatives we read the input string from right to
left.
Example:
1. S = S + S
2. S = S - S
3. S = a | b |c
Input:
a-b+c
1. S = S - S
2. S = S - S + S
3. S = S - S + c
4. S = S - b + c
5. S = a - b + c
Parse tree
Example:
Production rules:
1. T= T + T | T * T
2. T = a|b|c
Input:
a*b+c
Step 1:
Step 2:
Step 3:
Step 4:
Step 5:
Ambiguity
A grammar is said to be ambiguous if there exists more than one leftmost derivation
or more than one rightmost derivative or more than one parse tree for the given input
string. If the grammar is not ambiguous then it is called unambiguous.
Example:
1. S = aSb | SS
2. S = ∈
For the string aabb, the above grammar generates two parse trees:
If the grammar has ambiguity then it is not good for a compiler construction. No
method can automatically detect and remove the ambiguity but you can remove
ambiguity by re-writing the whole grammar without ambiguity.
What is BNF?
BNF stands for Backus–Naur Form which resulted primarily from the contributions
of John Backus and Peter Naur.
Left-hand side: Here we write a non-terminal to define it. In the above example, it
is <something>.
::=: This character group separates the Left hand side from Right hand side. Read
this symbol as "is defined as".
Right-hand side: The definition of the non-terminal specified on the right-hand side.
In the above example, it's "content".
The above <something> is just one thing fixed thing. Let's now see all the ways you
can compose a non-terminal.
Sequencing
Choice
You can just write a combination of one or more terminals or non-terminals in a
sequence and the result is their concatenation, with non-terminals being replaced by
their content. For example, you can express your breakfast in the following ways:
Let's say someday you want to drink coffee instead of tea. In this case, you can
express your possible breakfast items like below:
The | operator indicates that the parts separated by it are choices. there is no difference
between "tea" | "coffee and "coffee" | "tea".
As a simple example let's see how you express one or more digits in BNF:
<digits> ::= <digit> | <digit> <digits><digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" |
"7" | "8" | "9"
What is EBNF?
BNF is fine, but sometimes it can become verbose and hard to interpret. EBNF (which
stands for Extended Backus–Naur Form) may help you in those cases. For example,
the previous example can be written in EBNF like below:
digits = digit { digit }digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
The braces above mean that its inner part may be repeated 0 or more times. It frees
your mind from getting lost in recursion.
One interesting fact is that everything you can express in EBNF can also be expressed
in BNF.
EBNF usually uses a slightly different notation than BNF. For example:
Don't assume that these styles to be universal. There are several variants of them and
they are usually clear from the context. The more important thing to focus on is the
new operations it offers like the braces we've seen above.
Repetition
Curly braces indicate the inner content may be repeated 0 or more times. You have
already seen a good example of it above. Below is a very simple one just to make the
idea solid in your mind:
Grouping
Parentheses can be used to indicate grouping. It means everything they wrap can be
replaced with any of the valid strings that the contents of the group represent
according to the rules of EBNF. For example:
With BNF we could not do that in one line. It would look like the following in BNF:
Attribute Grammar
Example:
The right part of the CFG contains the semantic rules that specify how the grammar
should be interpreted. Here, the values of non-terminals E and T are added together
and the result is copied to the non-terminal E.
Semantic attributes may be assigned to their values from their domain at the time of
parsing and evaluated at the time of assignment or conditions. Based on the way the
attributes get their values, they can be broadly divided into two categories :
synthesized attributes and inherited attributes.
Synthesized attributes
These attributes get values from the attribute values of their child nodes. To illustrate,
assume the following production:
S → ABC
If S is taking values from its child nodes (A,B,C), then it is said to be a synthesized
attribute, as the values of ABC are synthesized to S.
As in our previous example (E → E + T), the parent node E gets its value from its
child node. Synthesized attributes never take values from their parent nodes or any
sibling nodes.
Inherited attributes
In contrast to synthesized attributes, inherited attributes can take values from parent
and/or siblings. As in the following production,
S → ABC
A can get values from S, B and C. B can take values from S, A, and C. Likewise, C
can take values from S, A, and B.
The data type specifies the size and type of information the variable will store.
float 4 bytes Stores fractional numbers, containing one or more decimals. Sufficien
6-7 decimal digits
double 8 bytes Stores fractional numbers, containing one or more decimals. Sufficien
15 decimal digits
There are different format specifiers for each data type. Here are some of them:
%d or %i int
%f or %F float
%lf double
%c char
%s Used for strings (text), which you will learn more about in a late
A primitive data type specifies the size and type of variable values, and it has no
additional methods.
Union Types
A union is a type whose variables may store different type values at different
times during program execution.
Record vs Union
struct sample
int x;
float y;
char z;
};
union sample
int x;
float y;
char z;
};
union sample x;
The unions in these languages are called free unions, because programmers
are allowed complete freedom from type checking in their use.
union sample
int a;
float b;
};
union sample myunion;
float x;
myunion.a = 27;
x = myunion.b;
Type checking of unions requires that each union construct include a type
indicator. Such an indicator is called a tag, or discriminant, and a union with
a discriminant is called a discriminated union.
The first language to provide discriminated unions was ALGOL 68. They are
now supported by Ada, ML, Haskell, and F#.
Unions in Ada
e.g.
record
Filled : Boolean;
Color : Colors;
case Form is
Diameter : Float;
Left_Side : Integer;
Right_Side : Integer;
Angle : Float;
Side_1 : Integer;
Side_2 : Integer;
end case;
end record;
Figure_1 : Figure;
Here,
Unions in F#
type intReal =
| IntValue of int
| RealValue of float;;
record
case Tag is
end case;
end record;
Creating Pointers
You learned from the previous chapter, that we can get the memory address of a
variable with the reference operator &:
Example
A pointer is a variable that stores the memory address of another variable as its
value.
A pointer variable points to a data type (like int) of the same type, and is created
with the * operator.
The address of the variable you are working with is assigned to the pointer:
Example
int myAge = 43; // An int variable
int* ptr = &myAge; // A pointer variable, with the name ptr, that stores the address of
myAge
Dereference
In the example above, we used the pointer variable to get the memory address of a
variable (used together with the & reference operator).
You can also get the value of the variable the pointer points to, by using the * operator
(the dereference operator):
Example
// Reference: Output the memory address of myAge with the pointer (0x7ffe5367e044)
printf("%p\n", ptr);
List
Lists are one of 4 built-in data types in Python used to store collections of data, the
other 3 are Tuple, Set, and Dictionary, all with different qualities and usage.
Example
Create a List:
List Items
List items are indexed, the first item has index [0], the second item has index [1] etc.
Ordered
When we say that lists are ordered, it means that the items have a defined order, and
that order will not change.
If you add new items to a list, the new items will be placed at the end of the list.
Changeable
The list is changeable, meaning that we can change, add, and remove items in a list
after it has been created.
Allow Duplicates
Since lists are indexed, lists can have items with the same value:
Example
List Length
To determine how many items a list has, use the len() function:
Example
Try it Yourself »
Example
Example
Tuple
Tuple is one of 4 built-in data types in Python used to store collections of data, the
other 3 are List, Set, and Dictionary, all with different qualities and usage.
Example
Create a Tuple:
thistuple = ("apple", "banana", "cherry")
print(thistuple)
Tuple Items
Tuple items are indexed, the first item has index [0], the second item has
index [1] etc.
Ordered
When we say that tuples are ordered, it means that the items have a defined order, and
that order will not change.
Unchangeable
Tuples are unchangeable, meaning that we cannot change, add or remove items after
the tuple has been created.
Allow Duplicates
Since tuples are indexed, they can have items with the same value:
Example