Understanding Syntax and Semantics Explained

·

7 min read

If you're learning a language, you've probably heard the word "syntax" and deal with it all the time. (Fucking Syntax Error).

It was a few nights ago that I was thinking to myself, I never followed programming paradigms and techniques seriously, and today I started to learn from the smallest topics that I always hear (even if I already remember). I created this repository. The learning path I took is characteristic (in the LEARNING_LIST.md file). let's pass, In contrast to the natural languages, with which we communicate our thoughts and feelings, programming languages can be viewed as artificial languages defined by men and women initially for the purpose of communicating with computers but, as importantly, for communicating algorithms among people. For example, language definitions consist of three components:

  • Syntax: the syntax of a programming language is the set of rules that define the combinations of symbols that are considered to be correctly structured programs in that language. The syntax of a language defines its surface form. Text-based programming languages are based on sequences of characters . The lexical grammar of a textual language specifies how characters must be chunked into tokens Syntax refers to the ways symbols may be combined to create well-formed sentences (or programs) in the language. Syntax defines the formal relations between the constituents of a language, thereby providing a structural description of the various expressions that make up legal strings in the language. Syntax deals solely with the form and structure of symbols in a language without any consideration given to their meaning, syntax is the concept that concerns itself only whether or not the sentence is valid for the grammar of the language

  • Semantics: Semantics is about whether or not the sentence has a valid meaning. Semantics reveals the meaning of syntactically valid strings in a language. For natural languages, this means correlating sentences and phrases with the objects, thoughts, and feelings of our experiences. For programming languages, semantics describes the behavior that a computer follows when executing a program in the language. We might disclose this behavior by describing the relationship between the input and output of a program or by a step-by-step explanation of how a program will execute on a real or an abstract machine. Semantics is the general term for the study of meaning. In computer science, the subject of programming language semantics seeks to give precise mathematical meaning to programs.

Low-Level Semantics:

In natural languages, a sentence can be grammatically correct but semantically meaningless. For example, the sentence:

"The man bought the infinity from the store."

This is grammatically correct but makes no sense in the real world. Similarly, in programming languages, a statement can be syntactically correct but semantically incorrect because it violates the language’s rules or its intended meaning.

At the low-level semantics of programming, we're concerned with whether a statement that has correct syntax also makes sense according to the language's type system or other rules. Even though the syntax might be valid, the semantic meaning of the operation could be invalid. The type system in statically-typed languages, like Java, helps enforce these rules before runtime, but dynamically-typed languages like JavaScript don't always enforce these rules until runtime.

Example in JavaScript:

In JavaScript, which is loosely typed, you might not have the protection that comes with a stricter type system, and the language will allow certain operations that don't make sense in a semantically meaningful way. Consider the following JavaScript code:

let name = "Alice";
name = 42;  // No error, but semantically this doesn't make sense.

Here, the syntax is perfectly valid, and JavaScript allows the assignment, but semantically it's odd. You're trying to assign a number (42) to a variable that is presumably intended to hold a string (name). There’s no type-checking in JavaScript to stop you from making this mistake, but it’s a low-level semantic error because it's inconsistent with the developer's intent. If we implement it. It is executed. without any error!

In stricter languages, like TypeScript or Java, this would trigger a type error immediately during compilation.

Example in TypeScript:

TypeScript, a superset of JavaScript, introduces type-checking to prevent these low-level semantic issues:

let name: string = "Alice";
name = 42;  // Error: Type 'number' is not assignable to type 'string'.

In TypeScript, the compiler detects the semantic error because 42 is a number and cannot be assigned to a variable declared as a string. This enforcement of types protects the developer from unintended mistakes.

In JavaScript, to avoid these low-level semantic issues, developers often use runtime checks:

let name = "Alice";

if (typeof name !== "string") {
    throw new Error("Expected a string!");
}

Even though JavaScript doesn't enforce types, adding manual checks can help avoid errors where incorrect types lead to problems in the program.


High-Level Semantics:

At a higher level, semantics is concerned with what your program is supposed to achieve. It's not just about whether the program has valid syntax or whether the types align correctly—it’s about whether the program behaves in the way the developer intended, or solves the problem it was designed to address.

For example, let's say you're building a simple stock trading system. The high-level semantics of your code are about making sure that the system trades stocks in the right way, with the correct business logic. Even if the code doesn't produce type errors or syntax errors, it could still fail to meet the intended functionality.

Example in JavaScript:

Let’s look at a JavaScript example of high-level semantics using a simplified stock trading scenario:

let openTrade = {
    symbol: "EURUSD",
    direction: "buy",
    profit: 100
};

function closeTrade(trade) {
    if (trade.profit >= 50) {
        console.log(`Closing trade for ${trade.symbol} with profit of ${trade.profit}`);
    } else {
        console.log(`Trade for ${trade.symbol} is not ready to close.`);
    }
}

// Check if a trade is open for EURUSD and close it if the profit target is reached.
closeTrade(openTrade);

The syntax is correct, and the program runs without errors. However, imagine we now introduce a mistake at a higher level, such as accidentally entering two trades for the same symbol, which violates the system's business rules.

let openTrades = [
    { symbol: "EURUSD", direction: "buy", profit: 100 },
    { symbol: "EURUSD", direction: "sell", profit: 20 }
];

// Check all trades and close any that hit their profit target.
openTrades.forEach(trade => closeTrade(trade));

Here, both trades are processed independently, but the system ends up with two trades on the same symbol, one in the buy direction and one in the sell direction. This breaks the high-level business rule that we should only have one open trade per symbol at any given time.

Although the code executes without syntax or type errors, it is semantically incorrect at a high level. The system logic should have ensured only one trade per symbol could be active at a time. This error would likely lead to unintended consequences in a real-world trading system, such as financial loss.

To fix this high-level semantic issue, you'd need to adjust the logic to ensure that only one trade is open for a particular symbol:

let openTrades = [
    { symbol: "EURUSD", direction: "buy", profit: 100 }
];

function openNewTrade(newTrade) {
    // Ensure no other trades are open for the same symbol.
    const existingTrade = openTrades.find(trade => trade.symbol === newTrade.symbol);
    if (!existingTrade) {
        openTrades.push(newTrade);
        console.log(`Opened new trade for ${newTrade.symbol}`);
    } else {
        console.log(`Cannot open a new trade for ${newTrade.symbol}, trade already exists.`);
    }
}

openNewTrade({ symbol: "EURUSD", direction: "sell", profit: 0 });

Here, the logic ensures that a new trade can't be opened if there’s already an active trade for the same symbol. This is a high-level semantic fix because it addresses the core business logic that the program is meant to follow, rather than a syntax or type issue.

  • Pragmatics: Pragmatics alludes to those aspects of language that involve the users of the language, namely psychological and sociological phenomena such as utility, scope of application, and effects on the users. For programming languages, pragmatics includes issues such as ease of implementation, efficiency in application, and programming methodology.

This was just basic information about each. For more information, you can read these items: