CSC305 Lecture Note 2
CSC305 Lecture Note 2
CSC305
A programming language can be described by the combination of its semantics, syntax and
pragmatic.
In the incorrect syntax, the missing colon will result in a syntax error because the language
expects a specific structure.
2. Semantics
Semantics refers to the meaning of a program or what the instructions do. A program with
correct syntax might still not make sense in terms of what it is trying to achieve, leading to
semantic errors or unintended behavior.
Example:
In the above example, the syntax is correct, but there is a semantic issue because dividing
by zero is mathematically undefined, leading to a runtime error. The meaning of dividing by
zero doesn't make sense, even though the program is syntactically correct.
3. Pragmatics
Pragmatics concerns how the language is intended to be used in practice, focusing on
efficiency, readability, and maintainability. Even if the code is syntactically and
semantically correct, it might not follow best practices or might be inefficient.
Example:
Both examples calculate the sum of numbers from 1 to 100. The first version is syntactically and
semantically correct, but the second version is more pragmatic because it’s shorter, easier to
read, and more efficient by using built-in functions.
Summary:
• Syntax: The rules for writing valid code (e.g., colons, indentation, parentheses).
• Semantics: The meaning of the code and what it does (e.g., logical errors like dividing by
zero).
• Pragmatics: Best practices and practical usage (e.g., efficient, readable code).
Explanation: This Python code is clear and simple, showing straightforward addition. The
function add_numbers has a clear purpose, and the code to call it is concise and readable.
2. Orthogonality
Orthogonality in a language means you can combine features in flexible ways without
unexpected results.
Example (Python):
Explanation: In this example, multiplication and addition work independently, so they can
be combined without any surprises. Python allows combining operators in meaningful
ways, demonstrating orthogonality.
3. Naturalness for the Application
The language should offer features that naturally align with the problem you’re solving. For
data analysis, for instance, Python’s list comprehensions are very useful.
Example (Python list comprehension for data filtering):
Explanation: Python’s list comprehensions are natural for data filtering, allowing you to
easily extract even numbers from a list. The syntax closely matches the problem, making it
a natural fit.
4. Support for Abstraction
Languages that support abstraction allow you to hide complex details behind simple
functions or classes.
Example (Python Class):
Explanation: The Rectangle class abstracts the details of area calculation. Instead of
dealing with width and height directly each time, you can just call rect.area() to get the
area.
5. Ease of Program Verification
A language with simple syntax makes it easier to check if the program does what it’s
supposed to. In Python, concise syntax helps in easily spotting errors.
Example (Simple conditional check):
Explanation: This function has a simple syntax for verifying if a number is even. The clear
logic helps easily verify its correctness, as there’s minimal complexity in the condition.
6. Programming Environment
An integrated development environment (IDE) like PyCharm or Visual Studio Code, with
features like autocompletion, debugging, and syntax highlighting, enhances productivity.
Example (Debugging in Visual Studio Code with breakpoints): In Visual Studio Code, you
can set a breakpoint in the is_even function (from the previous example) and run the
debugger to see how each line is executed, which helps in troubleshooting.
Explanation: Using a supportive IDE with debugging tools allows easy testing and
modification, especially in large projects. This makes it easier to identify and fix errors
quickly.
7. Portability of Programs
Python is cross-platform, meaning code can often run on different operating systems with
little to no modification.
Example (Simple script):
Explanation: A basic “Hello, World!” program written in Python runs on Windows, macOS,
and Linux without any changes. Python’s cross-platform compatibility makes it highly
portable.
Summary Table:
Compiled Interpreted Hybrid (Compiled + Interpreted)
Languages Languages
C Python Java
C++ JavaScript C#
Swift Lua
COBOL
Conclusion:
• Compiled languages offer high performance and are well-suited for system-level
programming but require compilation before execution.
• Interpreted languages offer flexibility and ease of use but generally have slower
execution due to line-by-line interpretation.
• Hybrid languages combine the best of both worlds, offering a balance between
flexibility and performance optimization.
• Size – Smaller than Second Generation Computers. Disk size mini computers.
• Speed – Relatively fast as compared to second generation, Million instructions per
second (MIPS).
• Cost – cost lower than Second generation.
• Language– High level languages like PASCAL, COBOL, BASIC, C etc.
• Reliability – Failure of circuits in Weeks.
• Power– Low power Consumption.
B) Trends and Developments in Computer Hardware
• Main Component – based on ULSI (Ultra Large scale integrated) Circuit. That is also
called Parallel Processing method.
• Memory – Optical disk and magnetic disk.
• Input Media – Speech input, Tactile input.
• Output Media – Graphics displays, Voice responses.
• Example – Lap-Tops, palm –Tops, Note books, PDA (personal Digital Assistant) etc.
Language Paradigm
Paradigm is a model or world view. Paradigms are important because they define a
programming language and how it works. A great way to think about a paradigm is as a set
of ideas that a programming language can use to perform tasks in terms of machine-code
at a much higher level. These different approaches can be better in some cases, and worse
in others.
Paradigm-Based Classification
1. Imperative Programming (Python)
Imperative programming involves giving step-by-step commands to solve a problem.
Explanation: In imperative programming, we explicitly tell the computer how to perform a
task, step-by-step. Here, we initialize total to zero and use a for loop to iterate over the
numbers 1 to 5, adding each number to total. The result is printed at the end.
• Key Paradigm Concept: We are directly manipulating total (a variable) and using
commands in a specific order to achieve the desired outcome.
• Output: Sum: 15
2. Functional Programming (Python)
• Functional programming focuses on using functions and avoids changing states.
Python supports this style with functions like map, filter, and lambda.
Explanation: Object-oriented programming organizes code into objects that contain both
data and behavior. Here:
• We define a Car class with attributes make and model.
• __init__ is the constructor method that initializes a new Car instance with specified
make and model.
• The display method prints out the car's make and model.
car1 = Car("Toyota", "Camry") creates an instance of Car, and car1.display() calls the
display method for this instance.
• Key Paradigm Concept: An object (car1) combines both state (make, model) and
behavior (display() method).
• Output: Car: Toyota Camry
4. Concurrent Programming (Python with threading)
• Concurrent programming allows multiple tasks to run at the same time. Here’s a
simple example using Python's threading library.
Explanation: Concurrent programming allows tasks to run in parallel. In this code:
• print_numbers prints numbers from 1 to 5.
• print_letters prints letters A to E.
By using threading. Thread, we create two threads (thread1 and thread2). start() begins
each thread, and join() waits for both threads to finish.
• Key Paradigm Concept: Threads allow tasks to overlap, demonstrating parallel
execution.
• Output: Interleaved numbers and letters, e.g., Number: 1, Letter: A, Number: 2,
etc., though the exact sequence can vary.
5. Logic Programming (Prolog-style Logic in Python)
• Python doesn’t natively support logic programming, but simple rule-based logic can
be emulated.
Explanation: In logic programming, the focus is on defining relationships. Here:
• We create a family dictionary to define parent-child relationships.
• The function is_parent (child, parent) checks if parent is the parent of child by
looking it up in the dictionary.
Key Paradigm Concept: Relationships are defined, and the code “asks” if a specific
relationship exists (i.e., is_parent ("john", "mary")).
• Output:
o True (John is Mary’s parent),
o True (Mary is Anna’s parent),
o False (Anna is not John’s parent).
6. Scripting Programming (Bash example shown in Python)
• Scripting languages are often used for automating tasks and manipulating text.
Grammar
Grammar in programming, is a set of rules that defines the structure of valid programs.
1. Purpose of Grammar in Programming
• Grammar is used to describe the structure of programming languages, which
allows the compiler (or interpreter) to transform code (a sequence of characters)
into a syntax tree.
• This syntax tree represents the logical structure of the code, showing the
relationships between elements, which is crucial for the compiler to translate the
code into machine code (for execution) or to type check (ensuring data types are
used correctly).
2. Backus-Naur Form (BNF)
• BNF is a notation system invented by John Backus and improved by Peter Naur for
describing programming language grammar.
• In BNF, a grammar is defined by four main components represented as (T, N, P, S):
o T (Tokens): The basic symbols or “vocabulary” of the language, like
keywords (e.g., while, for) and symbols (+, ().
o N (Nonterminals): These aren’t actual language elements but help define
the structure of the syntax tree. They are often enclosed in < >, like
<expression>.
o P (Production Rules): Rules that define how nonterminals and tokens
combine. These rules follow a format like <non-terminal> ::= <expression>,
where ::= is a separator, and | is used for multiple options.
o S (Start Symbol): A special non-terminal from which all grammatically valid
programs begin their structure.
3. Example Grammar for Arithmetic Expressions
The provided example grammar recognizes simple arithmetic expressions involving
addition and multiplication, as well as parentheses and variables (a, b, c):
In this example:
• <exp> represents an expression.
• It shows that an expression could be:
o Another expression added to an expression (<exp> + <exp>)
o An expression multiplied by another (<exp> * <exp>)
o An expression in parentheses
o Or a single value like "a", "b", or "c".
Example Expressions Valid in This Grammar
• a+b
• a*b+c
• (a + b) * (c + a)
• ((a * b) + c)
4. Types of Grammars and Chomsky’s Hierarchy
• Context-free grammars are the primary type used in programming languages since
they handle nested structures (like expressions within parentheses).
• Other grammar types in Chomsky’s hierarchy (developed by Noam Chomsky)
include:
o Regular grammars: Recognize simpler patterns, often used in lexical
analysis (the process of breaking down code into tokens).
o Unrestricted grammars: Very powerful and as flexible as Turing machines
(which can theoretically solve any problem given enough time).
In summary, grammars are the foundation of how compilers understand and process
programming languages, and they use context-free grammars to build syntax trees for
code interpretation.
2. Modulus (%)
The modulus operator % returns the remainder when one number is divided by another.
• Example:
• What are context-free grammars, and why are they commonly used in
programming language design?
Context-free grammars (CFGs) are grammars where production rules have a single
nonterminal on the LHS, allowing rules to be applied independently of context. CFGs are
widely used in programming languages because they can describe many structures (like
nested loops and conditionals) effectively, making them suitable for parsing languages. An
example is <expression> ::= <expression> + <term> | <term>.
• What is a parse tree, and how does it represent the structure of a program?
A parse tree, also known as a derivation tree, is a tree structure that represents the
syntactic structure of a string according to a given grammar. In a parse tree, each internal
node corresponds to a nonterminal symbol, and each leaf node corresponds to a terminal
symbol from the grammar. The tree illustrates how a start symbol of a grammar can be
expanded into the terminals by applying production rules, representing how the program or
expression is structured syntactically.
• Given the grammar below, draw a parse tree for the expression a + b * c.
• Explain why parse trees are useful in compilers and interpreters.
Parse trees are essential in compilers and interpreters because they provide a hierarchical
structure that represents the order in which operations and constructs should be
evaluated. They help compilers understand how each part of the code relates to the overall
syntax of the language. This structure enables error checking, optimization, and code
generation, as it clearly shows the relationships between expressions, operations, and
statements.
• In the context of parse trees, what is the difference between a leaf node and an
internal node?
In a parse tree, a leaf node represents a terminal symbol, which is an actual character or
token in the language (such as a number, operator, or keyword). A nonterminal is typically
represented by an internal node, which is expanded into other nodes (either terminals or
nonterminals) according to the production rules. Internal nodes define the structure of the
language, while leaf nodes represent the smallest units of syntax.