Home / Indice sezione
 www.icosaedro.it 

 PHP EBNF Syntax

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.

Indice

Cos'è l'EBNF
Come il PHP analizza ed esegue il programma
La sintassi EBNF del PHP
Riferimenti
Argomenti correlati

Cos'è l'EBNF

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 il PHP analizza ed esegue il programma

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!

La sintassi EBNF del PHP

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.

Riferimenti

Argomenti correlati


Umberto Salsi
Commenti
Contatto
Mappa
Home / Indice sezione
Still no comments to this page. Use the Comments link above to add your contribute.