0Pricing
C Academy · Lezione

Tokenizzare l'input

Trasformi il testo in token.

Tokenizzare l'input è una lezione C Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C Academy include 4 lezioni in totale.

Che cos'è un tokenizer?

Un interprete inizia trasformando il testo grezzo in token — le più piccole unità dotate di significato. Per la stringa 3 + 4 * 2, il tokenizer (o lexer) produce numeri e operatori.

Questa fase rimuove gli spazi bianchi e classifica ogni gruppo di caratteri, così il parser non deve mai gestire byte grezzi.

Un tipo di token

Rappresentiamo ogni token con un tag enum e un payload. I numeri contengono un valore intero; per operatori e parentesi è sufficiente il relativo tipo.

Mantenere il valore all'interno della struct evita di dover analizzare nuovamente il sorgente in seguito.

typedef enum {
  TOK_NUM, TOK_PLUS, TOK_MINUS,
  TOK_STAR, TOK_SLASH,
  TOK_LPAREN, TOK_RPAREN, TOK_EOF
} TokKind;

typedef struct {
  TokKind kind;
  int value; /* used when kind == TOK_NUM */
} Token;

Posizione di scansione

Il lexer percorre il sorgente con un puntatore cursore. Un piccolo helper esamina il carattere corrente senza consumarlo e restituisce '\0' alla fine.

L'aritmetica dei puntatori mantiene lo scanner veloce e semplice.

static const char *src;

static char peek(void) {
  return *src;
}

static char advance(void) {
  return *src++;
}

Saltare gli spazi bianchi

Prima di leggere un token eliminiamo spazi e tabulazioni. La funzione standard isspace di <ctype.h> gestisce ogni carattere di spazio.

Qui anche le nuove righe contano come spazi bianchi, quindi l'espressione può estendersi su più righe.

#include <ctype.h>

static void skip_ws(void) {
  while (isspace((unsigned char)peek()))
    advance();
}

Analizzare lessicalmente un numero

Quando il cursore si trova su una cifra, accumuliamo le cifre consecutive in un intero. Moltiplicare per dieci e aggiungere ogni cifra costruisce il valore da sinistra verso destra.

Il ciclo si interrompe al primo carattere non numerico, lasciando il cursore pronto per il token successivo.

static int lex_number(void) {
  int n = 0;
  while (isdigit((unsigned char)peek())) {
    n = n * 10 + (advance() - '0');
  }
  return n;
}

La funzione next_token

La routine principale salta gli spazi bianchi e poi seleziona l'azione in base al carattere corrente. Le cifre diventano un TOK_NUM; ogni operatore viene associato al proprio tipo.

Quando si raggiunge il NUL terminatore, viene prodotto TOK_EOF, il segnale che indica di fermarsi.

static Token next_token(void) {
  skip_ws();
  char c = peek();
  if (c == '\0') return (Token){TOK_EOF, 0};
  if (isdigit((unsigned char)c))
    return (Token){TOK_NUM, lex_number()};
  advance();
  switch (c) {
    case '+': return (Token){TOK_PLUS, 0};
    case '-': return (Token){TOK_MINUS, 0};
    case '*': return (Token){TOK_STAR, 0};
    case '/': return (Token){TOK_SLASH, 0};
    case '(': return (Token){TOK_LPAREN, 0};
    case ')': return (Token){TOK_RPAREN, 0};
  }
  return (Token){TOK_EOF, 0};
}

Eseguire il lexer

Ecco un programma completo che analizza lessicalmente un'espressione e stampa il tipo di ogni token. Per i numeri viene stampato anche il valore.

Noti che il ciclo termina quando legge TOK_EOF.

#include <stdio.h>
#include <ctype.h>

typedef enum { TOK_NUM, TOK_PLUS, TOK_STAR, TOK_EOF } TokKind;
typedef struct { TokKind kind; int value; } Token;

static const char *src;
static char peek(void){ return *src; }
static char advance(void){ return *src++; }

static Token next_token(void){
  while (isspace((unsigned char)peek())) advance();
  char c = peek();
  if (c=='\0') return (Token){TOK_EOF,0};
  if (isdigit((unsigned char)c)){
    int n=0; while(isdigit((unsigned char)peek())) n=n*10+(advance()-'0');
    return (Token){TOK_NUM,n};
  }
  advance();
  if (c=='+') return (Token){TOK_PLUS,0};
  return (Token){TOK_STAR,0};
}

int main(void){
  src = "12 + 3 * 4";
  Token t;
  do {
    t = next_token();
    if (t.kind==TOK_NUM) printf("NUM %d\n", t.value);
    else if (t.kind==TOK_PLUS) printf("PLUS\n");
    else if (t.kind==TOK_STAR) printf("STAR\n");
    else printf("EOF\n");
  } while (t.kind != TOK_EOF);
  return 0;
}

Un token di lookahead

Di solito i parser devono esaminare il token successivo prima di consumarlo. Memorizziamo un token in un current globale e lo ricarichiamo dopo ogni match.

Questo lookahead di un solo token è sufficiente per la nostra grammatica LL(1).

static Token current;

static void init_lexer(const char *s) {
  src = s;
  current = next_token();
}

static Token cur(void) { return current; }

static void bump(void) { current = next_token(); }

Segnalare gli errori lessicali

Un carattere sconosciuto — ad esempio $ o @ — non deve semplicemente scomparire. Un lexer robusto segnala il byte problematico e interrompe l'esecuzione.

Arrestarsi rapidamente nel lexer impedisce alle fasi successive di ricevere token spazzatura.

#include <stdio.h>
#include <stdlib.h>

static void lex_error(char c) {
  fprintf(stderr, "lex error: unexpected '%c'\n", c);
  exit(1);
}

Operatori composti da più caratteri

I linguaggi reali hanno token come == o <=. Per analizzarli lessicalmente, esaminiamo un carattere aggiuntivo dopo il primo.

Se il byte successivo completa l'operatore, consumiamo entrambi i caratteri; altrimenti produciamo la forma composta da un solo carattere.

/* fragment: distinguish '=' from '==' */
if (peek() == '=') {
  advance();
  if (peek() == '=') { advance(); /* TOK_EQ */ }
  else { /* TOK_ASSIGN */ }
}

Perché i token sono importanti

Raggruppando i caratteri in token, il parser lavora con un flusso pulito e tipizzato. Precedenza degli operatori, raggruppamento ed errori diventano tutti più semplici da comprendere.

Ora forniremo questi token a un parser a discesa ricorsiva.

Verifica rapida

Rifletta su ciò che il lexer produce per i simboli di raggruppamento.

Riepilogo

Ha costruito un lexer: un tipo di token, un cursore di scansione, la gestione degli spazi bianchi, l'analisi lessicale dei numeri e un dispatcher next_token con lookahead di un token.

Questi token sono l'input del parsing, che costruisce un albero sintattico a partire da essi.

Domande Frequenti

La lezione «Tokenizzare l'input» è gratuita?

Sì — il testo completo di «Tokenizzare l'input» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C Academy, passa a CoddyKit PRO. Il corso C Academy include 4 lezioni in totale.

Cosa imparerò in «Tokenizzare l'input»?

Trasformi il testo in token. Eserciti C Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C Academy?

Non è richiesta alcuna esperienza precedente. C Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Tokenizzare l'input»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C Academy?

Sì. Ogni lezione C Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Tokenizzare l'input
  2. Analizzare le espressioni
  3. Valutare l'albero
  4. Aggiungere variabili
← Torna a C Academy