Definición de gramática

Este capítulo dará algunas definiciones gramaticales relacionadas con Berry. Usamos Extended Backus Normal Form (EBNF) para definir o expresar la gramática. No usamos la gramática EBNF estricta para definir, pero hicimos muchas simplificaciones, pero estas simplificaciones no afectarán la comprensión de la gramática por parte de los lectores.

La definición EBNF de la gramática del lenguaje Berry es la siguiente:

(* program define *)
program = block;

(* block define *)
block = {statement};

(* statement define *)
statement = class_stmt | func_stmt | var_stmt | if_stmt | while_stmt |
         for_stmt | break_stmt | return_stmt | expr_stmt | import_stmt |
         try_stmt | throw_stmt | ';';
if_stmt = 'if' expr block {'elif' expr block} ['else' block] 'end';
while_stmt = 'while' expr block 'end';
for_stmt = 'for' ID ':' expr block 'end';
break_stmt = 'break' | 'continue';
return_stmt = 'return' [expr];

(* function define statement *)
func_stmt = 'def' ID func_body;
func_body = '(' [arg_field {',' arg_field}] ')' block 'end';
arg_field = ['*'] ID;

(* class define statement *)
class_stmt = 'class' ID [':' ID] class_block 'end';
class_block = {'var' ID {',' ID} | 'static' ['var'] ID ['=' expr] {',' ID ['=' expr] } | 'static' func_stmt | func_stmt};
import_stmt = 'import' (ID (['as' ID] | {',' ID}) | STRING 'as' ID);

(* exceptional handling statement *)
try_stmt = 'try' block except_block {except_block} 'end';
except_block = except_stmt block;
except_stmt = 'except' (expr {',' expr} | '..') ['as' ID [',' ID]];
throw_stmt = 'raise' expr [',' expr];

(* variable define statement *)
var_stmt = 'var' ID ['=' expr] {',' ID ['=' expr]};

(* expression define *)
expr_stmt = expr [assign_op expr];
expr = suffix_expr | unop expr | expr binop expr | range_expr | cond_expr;
cond_expr = expr '?' expr ':' expr; (* conditional expression *)
assign_op = '=' | '+=' | '-=' | '*=' | '/=' |
            '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=';
binop = '<' | '<=' | '==' | '!=' | '>' | '>=' | '||' | '&&' |
        '<<' | '>>' | '&' | '|' | '^' | '+' | '-' | '*' | '/' | '%';
range_expr = expr '..' [expr]
unop = '-' | '!' | '~';
suffix_expr = primary_expr {call_expr | ('.' ID) | '[' expr ']'};
primary_expr = '(' expr ')' | simple_expr | list_expr | map_expr | anon_func | lambda_expr;
simple_expr =  INTEGER | REAL | STRING | ID | 'true' | 'false' | 'nil';
call_expr = '(' [expr {',' expr}] ')';
list_expr = '[' {expr ','} [expr] ']';
map_expr = '{' {expr ':' expr ','} [expr ':' expr] '}';
anon_func = 'def' func_body;

(* anonymous function *)
lambda_expr = '/' [arg_field {',' arg_field}] | {arg_field}] '->' expr;

El formato EBNF estándar se puede encontrar en materiales relacionados. Aquí hay una explicación de los detalles que necesitan atención al leer la gramática anterior. Los símbolos que han aparecido a la izquierda del signo igual son símbolos no terminales, y los demás son símbolos terminales. El terminador encerrado entre comillas ' es una cadena fija, que suele ser una palabra clave u operador de idioma. Hay varios terminadores que son inconvenientes para describir directamente en EBNF: INTEGER representa el valor nominal del entero; REAL representa el valor nominal del número real; STRING representa el valor literal de cadena; ID representa el identificador. Estos terminadores se pueden definir mediante expresiones regulares:

  • ENTERO: 0x[a-fA-F0-9]+|\d+.

  • REAL: (\d+\.?|\.\d)\d*([eE][+-]?\d+)?.

  • CADENA: "(\\.|[^"])*"|'(\\.|[^'])*'.

  • ID: [_a-zA-Z]\w*

Los símbolos que aparecen secuencialmente en el EBNF estándar están separados por comas. Por intuición, uso espacios para implementar la función de coma. El símbolo de barra vertical “|” se pronuncia como “o”, significa que los patrones izquierdo y derecho solo pueden coincidir con uno de ellos, o tiene la prioridad más baja. Por ejemplo, la gramática a 0 a 1 |a 2 significa la fórmula correspondiente a 0 a 1 o la combinación a 2 . Los corchetes indican que la subexpresión dentro de los paréntesis coincide 0 o 1 veces, las llaves indican que la subexpresión interna coincide 0 o más veces, y los paréntesis solo tienen la función de tomar la subexpresión interna como un todo.

La siguiente es la definición de gramática JSON admitida por el módulo JSON en la biblioteca estándar de Berry. El uso de EBNF aún cumple con las convenciones anteriores:

json = value;
value = object | array |
        string | number | 'true' | 'false' | 'null';
object = '{' [ string ':' value ] { ',' string ':' value } '}';
array = '[' [json] { ',' json } ']';

Los símbolos no terminales cadena y número también se pueden definir mediante expresiones regulares. http://www.json.org proporciona la gramática estándar de JSON, que también incluye las definiciones de cadena y número. El soporte para números de la biblioteca Berry JSON es diferente del estándar. Los números JSON estándar deben comenzar con “-” o el número “0-9”, mientras que la biblioteca Berry JSON también acepta números que comienzan con un punto decimal.