Tokenizacja danych wejściowych
Przekształci Pan/Pani tekst w tokeny.
Tokenizacja danych wejściowych to bezpłatna lekcja C Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej C Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs C Academy zawiera 4 lekcji w sumie.
Czym jest tokenizer?
Interpreter rozpoczyna działanie od przekształcenia surowego tekstu w tokeny — najmniejsze znaczące jednostki. Dla ciągu 3 + 4 * 2 tokenizer (lub lexer) generuje liczby i operatory.
Ten etap usuwa białe znaki i klasyfikuje każdą grupę znaków, dzięki czemu parser nigdy nie pracuje na surowych bajtach.
Typ tokena
Modelujemy każdy token za pomocą znacznika typu enum i danych. Liczby przechowują wartość całkowitą, a operatory i nawiasy potrzebują jedynie informacji o swoim rodzaju.
Przechowywanie wartości wewnątrz struktury eliminuje konieczność ponownego skanowania źródła.
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;Pozycja skanowania
Lexer przechodzi po źródle za pomocą wskaźnika pozycji. Mała funkcja pomocnicza podgląda bieżący znak bez pobierania go i zwraca '\0' na końcu.
Arytmetyka wskaźników pozwala zachować szybkość i prostotę skanera.
static const char *src;
static char peek(void) {
return *src;
}
static char advance(void) {
return *src++;
}Pomijanie białych znaków
Przed odczytaniem tokena odrzucamy spacje i tabulatory. Standardowa funkcja isspace z <ctype.h> obsługuje każdy znak odstępu.
Znaki nowego wiersza są tutaj traktowane jako białe znaki, więc wyrażenie może zajmować wiele wierszy.
#include <ctype.h>
static void skip_ws(void) {
while (isspace((unsigned char)peek()))
advance();
}Analizowanie liczby
Gdy wskaźnik pozycji wskazuje cyfrę, gromadzimy kolejne cyfry w liczbie całkowitej. Mnożenie przez dziesięć i dodawanie każdej cyfry buduje wartość od lewej do prawej.
Pętla kończy się na pierwszym znaku niebędącym cyfrą, pozostawiając wskaźnik gotowy do odczytania następnego tokena.
static int lex_number(void) {
int n = 0;
while (isdigit((unsigned char)peek())) {
n = n * 10 + (advance() - '0');
}
return n;
}Funkcja next_token
Główna procedura pomija białe znaki, a następnie wybiera działanie na podstawie bieżącego znaku. Cyfry stają się tokenem TOK_NUM, a każdy operator otrzymuje własny rodzaj.
Dotarcie do kończącego ciąg NUL-a zwraca TOK_EOF — sygnał nakazujący zakończenie.
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};
}Uruchamianie lexera
Oto kompletny program, który tokenizuje wyrażenie i wyświetla rodzaj każdego tokena. Dla liczb wyświetla również ich wartość.
Zauważ, że pętla kończy się po odczytaniu 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;
}Jednotokenowe wyprzedzenie
Parsery zwykle muszą sprawdzić nadchodzący token przed jego pobraniem. Przechowujemy jeden token w globalnej zmiennej current i uzupełniamy ją po każdym dopasowaniu.
To jednotokenowe wyprzedzenie wystarcza dla naszej gramatyki 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(); }Zgłaszanie błędów leksykalnych
Nieznany znak — na przykład $ lub @ — nie powinien po cichu zniknąć. Solidny lexer zgłasza błędny bajt i przerywa działanie.
Natychmiastowe zakończenie na etapie lexera uniemożliwia późniejszym etapom otrzymanie błędnych tokenów.
#include <stdio.h>
#include <stdlib.h>
static void lex_error(char c) {
fprintf(stderr, "lex error: unexpected '%c'\n", c);
exit(1);
}Operatory wieloznakowe
Rzeczywiste języki mają tokeny takie jak == lub <=. Aby je rozpoznać, podglądamy dodatkowy znak po pierwszym.
Jeśli następny bajt dopełnia operator, pobieramy oba znaki; w przeciwnym razie generujemy formę jednoznakową.
/* fragment: distinguish '=' from '==' */
if (peek() == '=') {
advance();
if (peek() == '=') { advance(); /* TOK_EQ */ }
else { /* TOK_ASSIGN */ }
}Dlaczego tokeny są ważne
Po przekształceniu znaków w tokeny parser pracuje na uporządkowanym, typowanym strumieniu. Priorytety operatorów, grupowanie i błędy stają się łatwiejsze do zrozumienia.
Następnie przekażemy te tokeny do parsera rekurencyjnego opadania.
Szybkie sprawdzenie
Zastanów się, co lexer generuje dla symboli grupowania.
Podsumowanie
Zbudowałeś lexer: typ tokena, wskaźnik skanowania, pomijanie białych znaków, analizowanie liczb oraz funkcję next_token z jednotokenowym wyprzedzeniem.
Te tokeny są danymi wejściowymi parsera, który buduje z nich drzewo składniowe.
Często zadawane pytania
Czy lekcja „Tokenizacja danych wejściowych” jest bezpłatna?
Tak — pełny tekst „Tokenizacja danych wejściowych” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu C Academy, przejdź na CoddyKit PRO. Kurs C Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Tokenizacja danych wejściowych”?
Przekształci Pan/Pani tekst w tokeny. Ćwiczysz C Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć C Academy?
Nie wymagamy żadnego doświadczenia. C Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Tokenizacja danych wejściowych”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji C Academy?
Tak. Każda lekcja C Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Tokenizacja danych wejściowych
- Parsowanie wyrażeń
- Ewaluacja drzewa
- Dodawanie zmiennych