They belong to both worlds:
Syntax will describe which are the operators, which are primitive types (int
, float
), which are the keywords (return
, for
, while
). So syntax decides which "words" you can use in the programming language. With word I mean every single possible token: =
is a token, void
is a token, varName12345
is a token that is considered as an identifier, 12.4
is a token considered as a float and so on..
Semantics will describe how these tokens can be combined together inside you language.
For example you will have that while
semantics is something like:
WHILE ::= 'while' '(' CONDITION ')' '{' STATEMENTS '}'
CONDITION ::= CONDITION '&&' CONDITION | CONDITION '||' CONDITION | ...
STATEMENTS ::= STATEMENT ';' STATEMENTS | empty_rule
and so on. This is the grammar of the language that describes exactly how the language is structured. So it will be able to decide if a program is correct according to the language semantics.
Then there is a third aspect of the semantics, that is "what does that construct mean?". You can see it as a correspondence between, for example, a for
loop and how it is translated into the lower level language needed to be executed.
This third aspect will decide if your program is correct with respect to the allowed operations. Usually you can make a compiler reject many of programs that have no meaning (because they violates the semantic) but to be able to find many different mistakes you will have to introduce a new tool: the type checker that will also check that whenever you do operations they are correct according to the types.
For example you grammar can allow doing varName = 12.4
but the typechecker will use the declaration of varName
to understand if you can assign a float to it. (of course we're talking about static type checking)