Em poucas palavras
As linguagens de programação são compostas por uma sintaxe que representa o programa como cadeias de caracteres e uma semântica que é o significado pretendido do programa.
Linguagens formais são sintaxe sem significado. Destina-se a estudar a estrutura de conjuntos de cadeias definidas formalmente, sem geralmente atribuir significado a essas cadeias.
Expressão regular e outros formalismos (como Gramáticas livres de contexto) são usados para definir linguagens formais, usadas como componente sintático da programação e linguagens naturais, isto é, para representar sentenças de maneira estruturada. Outros mecanismos são usados para relacionar essa estrutura com a semântica das linguagens de programação.
Muito aqui é consideravelmente simplificado, principalmente em relação à linguagem natural.
Com muito mais detalhes
Para responder sua pergunta, devemos começar do começo. Uma linguagem no sentido usual é, informalmente, um meio de transmitir informações ou idéias. Em um idioma, geralmente se distingue entre sintaxe e semântica. Semântica é o que você quer falar / escrever. as informações que você deseja transmitir. Sintaxe é o meio que você usa para transmiti-lo, ou seja, uma representação convencional que pode ser trocada entre pessoas, e agora também entre pessoas e dispositivos, ou entre dispositivos (computadores).
Normalmente, você usará a palavra dog
para transmitir a idéia de um cachorro. A palavra dog
é composta de três letras, ou algum som equivalente, e pretende ser a representação de algum tipo de animal. A idéia principal é que a comunicação seja feita através da representação do que deve ser comunicado. As estruturas de representação são geralmente chamadas de sintaxe, enquanto o que é representado é chamado de semântica. Isso vale mais ou menos para a linguagem natural e também para as linguagens de programação.
As palavras são entidades sintáticas para representar conceitos semânticos mais ou menos elementares. Mas esses conceitos elementares precisam ser reunidos de várias maneiras para dar um significado mais complexo. Escrevemos
the dog
para transmitir que queremos dizer um cão específico e the dog bites the cat
para transmitir uma ideia mais complexa. Mas a maneira como as palavras são organizadas deve ser fixada por regras, para que possamos dizer qual cão e gato está mordendo o outro.
Portanto, temos regras como as sentence -> subject verb complement
que devem corresponder às frases e nos dizem como as idéias associadas a cada parte são articuladas. Essas regras são regras sintáticas, pois nos dizem como a representação de nossa mensagem deve ser organizada. O subject
próprio pode ser definido por uma regra subject -> article noun
e assim por diante.
2 x + 1 = 23x1 123
equation -> expression "=" expression
expression -> expression "+" expression
expression -> number
A estrutura das linguagens de programação é a mesma. As linguagens de programação são semanticamente especializadas em expressar cálculos a serem executados, em vez de expressar problemas a serem resolvidos, prova de teoremas ou relações amigáveis entre animais. Mas essa é a principal diferença.
As representações usadas na sintaxe são geralmente cadeias de caracteres ou sons para idiomas falados. A semântica geralmente pertence ao domínio abstrato, ou possivelmente à realidade, mas ainda abstrata em nossos processos de pensamento, ou ao domínio comportamental dos dispositivos. A comunicação implica a codificação da informação / idéia na sintaxe, que é transmitida e decodificada pelo receptor. O resultado é então interpretado de qualquer maneira pelo receptor.
Então, o que vemos da linguagem é principalmente sintaxe e sua estrutura. O exemplo acima é apenas uma das formas mais comuns de definir cadeias sintáticas e sua organização estrutural. Há outros. Para um determinado idioma, algumas strings podem ser atribuídas a uma estrutura e dizem pertencer ao idioma, enquanto outras não.
O mesmo vale para as palavras. Algumas seqüências de letras (ou som) são palavras legítimas, enquanto outras não.
Linguagens formais são apenas sintaxe sem semântica. Eles definem com um conjunto de regras quais seqüências podem ser construídas, usando os elementos básicos de um alfabeto. O que são as regras podem ser muito variáveis, às vezes complexas. Mas as linguagens formais são usadas para muitos propósitos matemáticos, além da comunicação lingüística, seja para linguagens naturais ou de programação. O conjunto de regras que define as seqüências de caracteres em um idioma é chamado de gramática. Mas há muitas outras maneiras de definir idiomas.
Na prática, uma linguagem é estruturada em dois níveis. O nível lexical define palavras construídas a partir de um alfabeto de caracteres. O nível sintático define sentenças ou programas construídos a partir de um alfabeto de palavras (ou mais precisamente de famílias de palavras, para que ele permaneça um alfabeto finito). Isso é necessariamente um pouco simplificado.
A estrutura das palavras é bastante simples na maioria das linguagens (programação ou natural), de modo que elas geralmente são definidas com o que geralmente é considerado o tipo mais simples de linguagem formal: as linguagens regulares. Eles podem ser definidos com expressões regulares (regexp) e são facilmente identificados com dispositivos programados chamados autômatos de estados finitos. Nos casos de linguagens de programação, exemplos de uma palavra são um identificador, um número inteiro, string, um número real, uma palavra reservada como if
or repeat
, um símbolo de pontuação ou um parêntese aberto. Exemplos de famílias de palavras são identificador, sequência, número inteiro.
O nível sintático é geralmente definido por um tipo de linguagem formal um pouco mais complexa: as linguagens sem contexto, usando as palavras como alfabeto. As regras que vimos acima são regras sem contexto para a linguagem natural. No caso das linguagens de programação, as regras podem ser:
statement -> assignment
statement -> loop
loop -> "while" expression "do" statement
assignment -> "identifier" "=" expression
expression -> "identifier"
expression -> "integer"
expression -> expression "operator" expression
Com essas regras, você pode escrever:
while aaa /= bbb do aaa = aaa + bbb / 6
o que é uma afirmação.
E a maneira como foi produzida pode ser representada por uma estrutura de árvore chamada árvore de análise ou árvore de sintaxe (não concluída aqui):
statement
|
_______________ loop _______________
/ / \ \
"while" expression "do" statement
__________|_________ |
/ | \ assignment
expression "operator" expression _______|_______
| | | / | \
"identifier" "/=" "identifier" "identifier" "=" expression
| | | |
aaa bbb aaa ... ...
Os nomes que aparecem à esquerda de uma regra são chamados de não terminais, enquanto as palavras também são chamadas de terminais, pois estão no alfabeto do idioma (acima do nível lexical). Não terminal representa as diferentes estruturas sintáticas, que podem ser usadas para compor um programa.
Essas regras são chamadas livres de contexto, porque um não terminal pode ser substituído arbitrariamente usando qualquer uma das regras correspondentes, independentemente do contexto em que aparece. O conjunto de regras que define o idioma é chamado de gramática livre de contexto.
Na verdade, existem restrições quanto a isso, quando os identificadores precisam ser declarados primeiro ou quando uma expressão deve atender às restrições de tipo. Mas essa restrição pode ser considerada semântica, e não sintática. Na verdade, alguns profissionais os colocam no que chamam de
semântica estática .
Dada qualquer sentença, qualquer programa, o significado dessa sentença é extraído analisando a estrutura dada pela árvore de análise para essa sentença. Portanto, é muito importante desenvolver algoritmos, chamados analisadores, que podem recuperar a estrutura em árvore correspondente a um programa, quando determinado.
O analisador é precedido pelo analisador lexical que reconhece as palavras e determina a família à qual elas pertencem. Em seguida, a sequência de palavras, ou elementos lexicais, é fornecida ao analisador que recupera a estrutura da árvore subjacente. A partir dessa estrutura, o compilador pode determinar como gerar código, que é a parte semântica do processamento do programa no lado do compilador.
O analisador de um compilador pode realmente construir uma estrutura de dados correspondente à árvore de análise e passá-la para os estágios posteriores do processo de compilação, mas não precisa. A execução do algoritmo de análise equivale a desenvolver uma estratégia computacional para explorar a árvore de sintaxe implícita no texto do programa. Essa árvore de sintaxe / análise pode ou não ser explicitada no processo, dependendo da estratégia de compilação (número de estágios). O que é necessário, porém, é que, em última instância, exista pelo menos uma exploração de baixo para cima da árvore de análise, explicitada ou deixada implícita na estrutura de computação.
A razão disso, intuitivamente, é que uma maneira formal padrão de definir semântica associada a uma estrutura de árvore sintática é por meio do que é chamado de homomorfismo. Não tema a grande palavra. A idéia é apenas considerar o significado do todo que é construído a partir do significado das partes, com base no operador que as conecta
Por exemplo, a sentença the dog bites the cat
pode ser analisada com a regra sentence -> subject verb complement
. Conhecer o significado dos 3 sub-árvores subject
, verb
e complement
, a regra que compõe-los nos diz que o sujeito está fazendo a ação, e que o gato é o único que está mordido.
Essa é apenas uma explicação intuitiva, mas pode ser formalizada. A semântica é construída para cima a partir dos constituintes. Mas isso esconde muita complexidade.
O trabalho interno de um compilador pode ser decomposto em vários estágios. O compilador real pode funcionar etapa por etapa, usando representações intermediárias. Também pode mesclar algumas etapas. Isso depende da tecnologia usada e da complexidade da compilação do idioma em questão.