I've created a new type specifier ("local") for local variables (which can be declared at the beginning of a function and are valid only in that function) and another type specifier for arrays ("array"). I've removed the other type specifiers from the grammar, as the first word of each object pushed onto the stack determines the object type. I've also added Boolean data types. Below is the EBNF grammar for the
mC programming language (tokens are in uppercase/quotation marks):
Code:
program -> code ENDFILE
code -> { array-declaration } { function-declaration } main-function-declaration
array-declaration -> ARRAY ID dimensions SEMICOLON
dimensions -> LEFTBRACKET NUM RIGHTBRACKET { LEFTBRACKET NUM RIGHTBRACKET }
function-declaration -> ID LEFTPARENTHESIS params RIGHTPARENTHESIS LEFTBRACE local-declarations statement-list RIGHTBRACE
params -> [ param { COMMA param } ]
param -> ID { LEFTBRACKET NUM RIGHTBRACKET }
main-function-declaration -> MAIN LEFTBRACE local-declarations statement-list RIGHTBRACE
local-declarations -> { LOCAL var-declaration }
var-declaration -> ID SEMICOLON | array-declaration
statement-list -> { statement }
statement -> compound-stmt | if-stmt | while-stmt | do-stmt | for-stmt | switch-stmt | return-stmt | read-stmt | write-stmt | assignment-stmt | expression-stmt | goto-stmt | label-stmt | nop-stmt
compound-stmt -> LEFTBRACE statement-list RIGHTBRACE
if-stmt -> IF LEFTPARENTHESIS Boolean-expression RIGHTPARENTHESIS statement [ ELSE statement ]
while-stmt -> WHILE LEFTPARENTHESIS Boolean-expression RIGHTPARENTHESIS statement
do-stmt -> DO statement WHILE LEFTPARENTHESIS Boolean-expression RIGHTPARENTHESIS SEMICOLON
for-stmt -> FOR LEFTPARENTHESIS [ assignment ] SEMICOLON Boolean-expression SEMICOLON [ addditive-expression ] RIGHTPARENTHESIS statement
switch-stmt -> SWITCH LEFTPARENTHESIS ID { LEFTBRACKET additive-expression RIGHTBRACKET } RIGHTPARENTHESIS LEFTBRACE { CASE NUM COLON statement-list BREAK SEMICOLON } [ DEFAULT COLON statement-list ] RIGHTBRACE
return-stmt -> RETURN [ expression ] SEMICOLON
read-stmt -> READ ID { LEFTBRACKET additive-expression RIGHTBRACKET } SEMICOLON
write-stmt -> WRITE expression SEMICOLON
assignment-stmt -> assignment SEMICOLON
expression-stmt -> [ expression ] SEMICOLON
goto-stmt -> GOTO ID SEMICOLON
label-stmt -> ID COLON statement
nop-stmt -> NOPR SEMICOLON
assignment -> ID { LEFTBRACKET additive-expression RIGHTBRACKET } "=" expression
expression -> Boolean-expression | additive-expression
Boolean-expression -> logical-expression { boolop logical-expression }
logical-expression -> additive-expression relop additive-expression | ID { LEFTBRACKET additive-expression RIGHTBRACKET } | TRUE | FALSE
additive-expression -> term { addop term }
term -> [ NEGATE ] factor { mulop [ NEGATE ] factor }
factor -> LEFTPARENTHESIS additive-expression RIGHTPARENTHESIS | NUM | ID { LEFTBRACKET additive-expression RIGHTBRACKET } | ID LEFTPARENTHESIS args RIGHTPARENTHESIS | ID "++" | ID "--"
args -> [ expression { COMMA expression } ]
boolop -> "and" | "or" | "not"
relop -> "<=" | "<" | ">" | ">=" | "==" | "!="
addop -> "+" | "-"
mulop -> "*" | "/"
Some test programs:
1) factorial using a while loop
Code:
/*factorial program 1*/
main{
read x;
factorial=1;
while (x>0) {
factorial = factorial*x;
x--;
}
write factorial;
}
2) factorial using recursion
Code:
/*factorial program 2*/
fact(x){
if (x==1) return 1;
else return x*fact(x-1);
}
main{
read y;
if (y>1) fact(y);
else write 1;
}
3) squaring a number using nested for loops
Code:
/*square program*/
main{
read x;
product=0;
for(i=x;i>=1;i--) {
for(j=1;j<=x;j++) {
product++;
}
}
write product;
}