Home / Indice sezione | www.icosaedro.it | ![]() |
Il manuale ufficiale del PHP descrive il linguaggio attraverso alcuni esempi concreti. Questo facilita l'apprendimento dei rudimenti, ma poi sorgono dei dubbi sulla esatta sintassi di certi elementi. Quello che manca alla documentazione ufficiale è una guida di riferimento che descrive la sintassi del linguaggio in modo rigoroso. Questo articolo tenta di colmare questa lacuna e presenta la sintassi EBNF del linguaggio PHP versione 5.2.0.
Cos'è l'EBNF
Come il PHP analizza ed esegue il programma
La sintassi EBNF del PHP
Riferimenti
Argomenti correlati
L'EBNF (Extended Backus-Naur Form) è un modo conciso e rigoroso per esprimere la sintassi di un linguaggio di programmazione. L'EBNF fu proposto da Niklaus Wirth (1977) come estensione del formalismo BNF utilizzato per la definizione del linguaggio Algol 60.
La sintassi EBNF descrive nel dettaglio l'ordine dei simboli e delle parole ammessi dal linguaggio. Ad esempio, un numero intero costituito da una sequenza di una o più cifre potrebbe essere definito così:
intero = cifra { cifra } ;
cifra = "0" .. "9" ;
La parola intero
è il nome che abbiamo dato a questa
entità del linguaggio; questa entità viene definita da
una regola che dice che il numero intero è formato da una cifra
alla quale seguono zero o più altre cifre. Le parentesi graffe
indicano che l'espressione al loro interno può essere ripetuta
zero o più volte. La seconda regola definisce l'entità
cifra
come intervallo dei caratteri che vanno dallo zero
al nove. Le stringhe incluse tra doppi apici sono costanti letterali,
cioè i caratteri del testo che devono corrispondere.
A volte ci sono delle parti opzionali della sintassi, cioè delle
sotto-espressioni la cui presenza è opzionale. Per indicare questo
fatto la sotto-espressione deve essere racchiusa tra parentesi quadrate.
Le alternative si esprimono con l'operatore |
(barra verticale).
Ad esempio, la seguente sintassi descrive i numeri a virgola mobile:
virgola_mobile = intero (decimali | esponente | decimali esponente);
segno = "+" | "-";
decimali = "." intero ;
esponente = "E" [segno] intero ;
Queste regole dicono che un numero in virgola mobile deve avere una parte intera seguita da una parte decimale oppure dall'esponente o da entrambi; in altri termini, se non è presente almeno il punto decimale oppure l'esponente, allora la sintassi non è corretta. Le parentesi tonde sono necessarie perché l'operatore "|" ha precedenza minore rispetto alla semplice sequenza di fattori. Notare che il segno dell'esponente è opzionale.
Il formalismo EBNF si può applicare per descrivere le regole che governano una successione di simboli qualsiasi, non solo singoli caratteri. A questo livello di astrazione gli elementi del linguaggio la cui sintassi viene descritta sono degli identificatori non definiti dalla sintassi stessa. Nelle applicazioni ai compilatori e ai validatori, in generale si costruisce prima uno scanner che trasforma la successione dei caratteri in simboli, e poi si costruisce il parser che analizza la successione dei simboli prodotti dallo scanner. La sintassi EBNF viene impiegata per descrivere la sintassi a livello di scanner o la sintassi a livello di parser, o entrambe le cose.
Come abbiamo accennato, i programmi che analizzano i sorgenti
dei linguaggi di programmazione in generale sono organizzati
in almeno due stadi: lo scanner e il parser. Lo scanner
analizza il testo sorgente e converte la sequenza dei caratteri
in simboli (detti anche token), cioè in codici
che rappresentano i vari elementi del linguaggio. La pagina http://it.php.net/manual/en/tokens.php
elenca i token prodotti dallo scanner dell'interprete PHP. Alcuni token
rappresentano le parole riservate del linguaggio, come include if
else switch echo
... Altri token rappresentano gli operatori,
come + - * / ++ --
... Altri token ancora rappresentano
parti variabili del linguaggio come i numeri o i nomi delle funzioni e
delle variabili.
Una volta scomposto il testo sorgente in una sequenza di token, il lavoro del parser risulta notevolmente semplificato. Lo scopo del parser è controllare la corretta successione dei simboli secondo la sintassi EBNF che presentiamo in questo articolo. In generale gli errori rilevati in questa fase sono fatali, cioè causano l'interruzione della esecuzione.
L'analisi svolta dal parser è indispensabile per guidare la fase successiva dell'interprete, cioè il compilatore. Il compilatore produce uno pseudo-codice che può essere eseguito direttamente dall'interprete dello pseudo-codice. Finalmente il nostro programma sta girando!
Il pacchetto sorgente del PHP include il file
Zend/zend_language_parser.y
che contiene la sintassi BNF
usata dall'analizzatore Yacc. Il BNF è piuttosto involuto ed
è difficile da leggere. Ecco perché l'ho tradotto in EBNF
e formattato per benino. Si tratta della sintassi relativa al PHP 5.2.0.
Perché solo il PHP 5 e non il PHP 4 che è ancora così
diffuso? Io penso che i principali fruitori di questo documento saranno
coloro che affrontano il linguaggio PHP per la prima volta, e quindi
meglio cominciare con la versione più recente disponibile.
Il file zend_language_parser.y da cui ho ricavato la sintassi EBNF si trova nel pacchetto sorgente del PHP, ed è disponibile anche in questo file:
php-syntax-yacc.txt
La sintassi EBNF da me prodotta si trova in questo file in formato testo:
php-syntax-ebnf.txt
Da questo testo ho ricavato la forma HTML che
riporto qui sotto usando il programma BNF_CHK (http://www.icosaedro.it/bnf_chk/).
Questo programma numera le regole e mette degli indici ai simboli in modo
da trovare rapidamente la regola che lo definisce. Alcuni simboli il cui
nome inizia con T_*
sono individuati dallo scanner del PHP e
non sono ulteriormente descritti in questa sintassi; vanno perciò
considerati come simboli primitivi.
IDs report:
PHP_SOURCE_TEXT:
defined in line 14,
NOT USED
T_INLINE_HTML:
NOT DEFINED,
used 1 times
T_ENCAPSED_AND_WHITESPACE:
NOT DEFINED,
used 1 times
T_CHARACTER:
NOT DEFINED,
used 1 times
1. PHP_SOURCE_TEXT = { inner_statement3 | halt_compiler_statement2 } ;
2. halt_compiler_statement =
"__halt_compiler"
"("
")"
";"
;3. inner_statement = statement5 | function_declaration_statement8 | class_declaration_statement9 ;
4. inner_statement_list = { inner_statement3 } ;
5. statement =
"{"
inner_statement_list4"}"
|"if"
"("
expr54")"
statement5 { elseif_branch23 } [ else_single25 ] |"if"
"("
expr54")"
":"
inner_statement_list4 { new_elseif_branch24 } [ new_else_single26 ]"endif"
";"
|"while"
"("
expr54")"
while_statement22 |"do"
statement5"while"
"("
expr54")"
";"
|"for"
"("
for_expr40";"
for_expr40";"
for_expr40")"
for_statement16 |"switch"
"("
expr54")"
switch_case_list20 |"break"
[ expr54 ]";"
|"continue"
[ expr54 ]";"
|"return"
[ expr_without_variable41 | variable58 ]";"
|"global"
global_var31 {","
global_var31 }";"
|"static"
static_var32 {","
static_var32 }";"
|"echo"
echo_expr_list39";"
| T_INLINE_HTML? | expr54";"
|"use"
use_filename7";"
|"unset"
"("
variable58 {","
variable58 }")"
";"
|"foreach"
"("
( variable58 | expr_without_variable41 )"as"
foreach_variable15 ["=>"
foreach_variable15 ]")"
foreach_statement17 |"declare"
"("
declare_list19")"
declare_statement18 |";"
|"try"
"{"
inner_statement_list4"}"
catch_branch6 { catch_branch6 } |"throw"
expr54";"
;6. catch_branch =
"catch"
"("
fully_qualified_class_name43 T_VARIABLE82")"
"{"
inner_statement_list4"}"
;7. use_filename = T_CONSTANT_ENCAPSED_STRING95 |
"("
T_CONSTANT_ENCAPSED_STRING95")"
;8. function_declaration_statement =
"function"
["&"
] T_STRING80"("
parameter_list27")"
"{"
inner_statement_list4"}"
;9. class_declaration_statement = class_entry_type10 T_STRING80 [ extends_from11 ] [ implements_list13 ]
"{"
{ class_statement33 }"}"
|"interface"
T_STRING80 [ interface_extends_list12 ]"{"
{ class_statement33 }"}"
;10. class_entry_type = [
"abstract"
|"final"
]"class"
;11. extends_from =
"extends"
fully_qualified_class_name43 ;12. interface_extends_list =
"extends"
interface_list14 ;13. implements_list =
"implements"
interface_list14 ;14. interface_list = fully_qualified_class_name43 {
","
fully_qualified_class_name43 } ;15. foreach_variable = [
"&"
] variable58 ;16. for_statement = statement5 |
":"
inner_statement_list4"endfor"
";"
;17. foreach_statement = statement5 |
":"
inner_statement_list4"endforeach"
";"
;18. declare_statement = statement5 |
":"
inner_statement_list4"enddeclare"
";"
;19. declare_list = T_STRING80
"="
static_scalar49 {","
T_STRING80"="
static_scalar49 } ;20. switch_case_list =
"{"
[";"
] { case_list21 }"}"
|":"
[";"
] { case_list21 }"endswitch"
";"
;21. case_list =
"case"
expr54 [":"
|";"
] inner_statement_list4 |"default"
[":"
|";"
] inner_statement_list4 ;22. while_statement = statement5 |
":"
inner_statement_list4"endwhile"
";"
;23. elseif_branch =
"elseif"
"("
expr54")"
statement5 ;24. new_elseif_branch =
"elseif"
"("
expr54")"
":"
inner_statement_list4 ;25. else_single =
"else"
statement5 ;26. new_else_single =
"else"
":"
inner_statement_list4 ;27. parameter_list = [ parameter28 {
","
parameter28 } ] ;28. parameter = [ T_STRING80 |
"array"
] ["&"
] T_VARIABLE82 ["="
static_scalar49 ] ;29. function_call_parameter_list = [ function_call_parameter30 {
","
function_call_parameter30 } ] ;30. function_call_parameter = expr_without_variable41 | variable58 |
"&"
w_variable56 ;31. global_var = T_VARIABLE82 |
"$"
r_variable55 |"$"
"{"
expr54"}"
;32. static_var = T_VARIABLE82 [
"="
static_scalar49 ] ;33. class_statement = variable_modifiers35 class_variable_declaration37 {
","
class_variable_declaration37 }";"
|"const"
class_constant_declaration38 {","
class_constant_declaration38 }";"
| { modifier36 }"function"
["&"
] T_STRING80"("
parameter_list27")"
method_body34 ;34. method_body =
";"
|"{"
inner_statement_list4"}"
;35. variable_modifiers =
"var"
| modifier36 { modifier36 } ;36. modifier =
"public"
|"protected"
|"private"
|"static"
|"abstract"
|"final"
;37. class_variable_declaration = (
"var"
| modifier36 { modifier36 } ) T_VARIABLE82 ["="
static_scalar49 ] ;38. class_constant_declaration = T_STRING80
"="
static_scalar49 ;39. echo_expr_list = expr54 {
","
expr54 } ;40. for_expr = [ expr54 {
","
expr54 } ] ;41. expr_without_variable =
"list"
"("
assignment_list70")"
"="
expr54 | variable58"="
expr54 | variable58"="
"&"
variable58 | variable58"="
"&"
"new"
class_name_reference44 [ ctor_arguments47 ] |"new"
class_name_reference44 [ ctor_arguments47 ] |"clone"
expr54 | variable58 ("+="
|"-="
|"*="
|"/="
|".="
|"%="
|"&="
|"|="
|"^="
|"<<="
|">>="
) expr54 | rw_variable57"++"
|"++"
rw_variable57 | rw_variable57"--"
|"--"
rw_variable57 | expr54 ("||"
|"&&"
|"or"
|"and"
|"xor"
|"|"
|"&"
|"^"
|"."
|"+"
|"-"
|"*"
|"/"
|"%"
|"<<"
|">>"
|"==="
|"!=="
|"<"
|"<="
|">"
|">="
) expr54 | ("+"
|"-"
|"!"
|"~"
) expr54 | expr54"instanceof"
class_name_reference44 |"("
expr54")"
| expr54"?"
expr54":"
expr54 | internal_functions77 |"(int)"
expr54 |"(double)"
expr54 |"(float)"
expr54 |"(real)"
expr54 |"(string)"
expr54 |"(array)"
expr54 |"(object)"
expr54 |"(bool)"
expr54 |"(boolean)"
expr54 |"(unset)"
expr54 |"exit"
[ exit_expr46 ] |"die"
[ exit_expr46 ] |"@"
expr54 | scalar51 |"array"
"("
[ array_pair_list72 ]")"
|"`"
encaps_list74"`"
|"print"
expr54 ;42. function_call = T_STRING80
"("
function_call_parameter_list29")"
| fully_qualified_class_name43"::"
T_STRING80"("
function_call_parameter_list29")"
| fully_qualified_class_name43"::"
variable_without_objects60"("
function_call_parameter_list29")"
| variable_without_objects60"("
function_call_parameter_list29")"
;43. fully_qualified_class_name = T_STRING80 ;
44. class_name_reference = T_STRING80 | dynamic_class_name_reference45 ;
45. dynamic_class_name_reference = base_variable63
"->"
object_property67 {"->"
object_property67 } | base_variable63 ;46. exit_expr =
"("
[ expr54 ]")"
;47. ctor_arguments =
"("
function_call_parameter_list29")"
;48. common_scalar = T_LNUMBER83 | T_DNUMBER90 | T_CONSTANT_ENCAPSED_STRING95 |
"__LINE__"
|"__FILE__"
|"__CLASS__"
|"__METHOD__"
|"__FUNCTION__"
;49. static_scalar = common_scalar48 | T_STRING80 |
"+"
static_scalar49 |"-"
static_scalar49 |"array"
"("
[ static_array_pair_list52 ]")"
| static_class_constant50 ;50. static_class_constant = T_STRING80
"::"
T_STRING80 ;51. scalar = T_STRING80 | T_STRING_VARNAME98 | class_constant78 | common_scalar48 |
"\""
encaps_list74"\""
|"'"
encaps_list74"'"
| T_START_HEREDOC100 encaps_list74 T_END_HEREDOC102 ;52. static_array_pair_list = static_array_pair53 {
","
static_array_pair53 } [","
] ;53. static_array_pair = static_scalar49 [
"=>"
static_scalar49 ] ;54. expr = r_variable55 | expr_without_variable41 ;
55. r_variable = variable58 ;
56. w_variable = variable58 ;
57. rw_variable = variable58 ;
58. variable = base_variable_with_function_calls62 [
"->"
object_property67 method_parameters59 {"->"
object_property67 method_parameters59 } ] ;59. method_parameters =
"("
function_call_parameter_list29")"
;60. variable_without_objects = reference_variable64 | simple_indirect_reference69 reference_variable64 ;
61. static_member = fully_qualified_class_name43
"::"
variable_without_objects60 ;62. base_variable_with_function_calls = base_variable63 | function_call42 ;
63. base_variable = reference_variable64 | simple_indirect_reference69 reference_variable64 | static_member61 ;
64. reference_variable = compound_variable65 { selector66 } ;
65. compound_variable = T_VARIABLE82 |
"$"
"{"
expr54"}"
;66. selector =
"["
[ expr54 ]"]"
|"{"
expr54"}"
;67. object_property = variable_name68 { selector66 } | variable_without_objects60 ;
68. variable_name = T_STRING80 |
"{"
expr54"}"
;69. simple_indirect_reference =
"$"
{"$"
} ;70. assignment_list = [ assignment_list_element71 ] {
","
[ assignment_list_element71 ] } ;71. assignment_list_element = variable58 |
"list"
"("
assignment_list70")"
;72. array_pair_list = array_pair73 {
","
array_pair73 } [","
] ;73. array_pair =
"&"
w_variable56 | expr54"=>"
"&"
w_variable56 | expr54"=>"
expr54 ;74. encaps_list = { encaps_var75 | T_STRING80 | T_NUM_STRING99 | T_ENCAPSED_AND_WHITESPACE? | T_CHARACTER? | T_BAD_CHARACTER81 |
"["
|"]"
|"{"
|"}"
|"->"
} ;75. encaps_var = T_VARIABLE82 [
"["
encaps_var_offset76"]"
] | T_VARIABLE82"->"
T_STRING80 |"${"
expr54"}"
|"${"
T_STRING_VARNAME98"["
expr54"]"
"}"
| T_CURLY_OPEN94 variable58"}"
;76. encaps_var_offset = T_STRING80 | T_NUM_STRING99 | T_VARIABLE82 ;
77. internal_functions =
"isset"
"("
variable58 {","
variable58 }")"
|"empty"
"("
variable58")"
|"include"
expr54 |"include_once"
expr54 |"eval"
"("
expr54")"
|"require"
expr54 |"require_once"
expr54 ;78. class_constant = fully_qualified_class_name43
"::"
T_STRING80 ;79. LABEL = ( letter89 |
"_"
) { letter89 | digit87 |"_"
} ;80. T_STRING = LABEL79 ;
81. T_BAD_CHARACTER =
"\x00".."\x08"
|"\x0b"
|"\x0c"
|"\x0e".."\x1f"
;82. T_VARIABLE =
"$"
T_STRING80 ;83. T_LNUMBER = octal84 | decimal85 | hexadecinal86 ;
84. octal =
"0"
{"0".."7"
} ;85. decimal =
"1".."9"
{ digit87 } ;86. hexadecinal =
"0x"
hexdigit88 { hexdigit88 } ;87. digit =
"0".."9"
;88. hexdigit = digit87 |
"a".."f"
|"A".."F"
;89. letter =
"a".."z"
|"A".."Z"
|"\x7f".."\xff"
;90. T_DNUMBER = DNUM91 | EXPONENT_DNUM92 ;
91. DNUM = digit87 [
"."
] digit87 { digit87 } | digit87 { digit87 } ["."
] { digit87 } ;92. EXPONENT_DNUM = ( LNUM93 | DNUM91 ) (
"e"
|"E"
) ["+"
|"-"
] LNUM93 ;93. LNUM = digit87 { digit87 } ;
94. T_CURLY_OPEN =
"${"
;95. T_CONSTANT_ENCAPSED_STRING = single_quoted_constant_string96 | double_quoted_constant_string97 ;
96. single_quoted_constant_string =
"'"
{"any char except ' and \\"
|"\\"
"any char"
}"'"
;97. double_quoted_constant_string =
"\""
{"any char except $ \" and \\"
|"\\"
"any char"
}"\""
;98. T_STRING_VARNAME = LABEL79 ;
99. T_NUM_STRING = LNUM93 | hexadecinal86 ;
100. T_START_HEREDOC =
"<<<"
{" "
|"\t"
} LABEL79 NEWLINE101 ;101. NEWLINE =
"\r"
|"\n"
|"\r\n"
;102. T_END_HEREDOC =
"FIXME: here at the beginning of the line"
LABEL79 [";"
] NEWLINE101 ;
Da ricordare: sono case-insensitive le parole riservate del linguaggio e i nomi di funzione, classi e metodi. Tutti gli altri identificatori sono case-sensitive.
Umberto Salsi | Commenti | Contatto | Mappa | Home / Indice sezione |
Still no comments to this page. Use the Comments link above to add your contribute.