You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

917 lines
24 KiB

/*
Copyright 2018 0KIMS association.
This file is part of circom (Zero Knowledge Circuit Compiler).
circom is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
circom is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with circom. If not, see <https://www.gnu.org/licenses/>.
*/
/* description: Construct AST for jaz language. */
/* lexical grammar */
%lex
%%
\s+ { /* skip whitespace */ }
\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\/ { /* console.log("MULTILINE COMMENT: "+yytext); */ }
\/\/.* { /* console.log("SINGLE LINE COMMENT: "+yytext); */ }
var { return 'var'; }
signal { return 'signal'; }
private { return 'private'; }
input { return 'input'; }
output { return 'output'; }
linearCombination { return 'linearCombination'; }
component { return 'component'; }
template { return 'template'; }
function { return 'function'; }
if { return 'if'; }
else { return 'else'; }
for { return 'for'; }
while { return 'while'; }
do { return 'do'; }
return { return 'return'; }
include { return 'include'; }
0x[0-9A-Fa-f]* { return 'HEXNUMBER'; }
[0-9]+ { return 'DECNUMBER'; }
[a-zA-Z][a-zA-Z$_0-9]* { return 'IDENTIFIER'; }
\"[^"]+\" { yytext = yytext.slice(1,-1); return 'STRING'; }
\=\=\> { return '==>'; }
\<\=\= { return '<=='; }
\-\-\> { return '-->'; }
\<\-\- { return '<--'; }
\=\=\= { return '==='; }
\>\>\= { return '>>='; }
\<\<\= { return '<<='; }
\&\& { return '&&'; }
\|\| { return '||'; }
\=\= { return '=='; }
\<\= { return '<='; }
\>\= { return '>='; }
\!\= { return '!='; }
\>\> { return '>>'; }
\<\< { return '<<'; }
\*\* { return '**'; }
\+\+ { return '++'; }
\-\- { return '--'; }
\+\= { return '+='; }
\-\= { return '-='; }
\*\= { return '*='; }
\/\= { return '/='; }
\%\= { return '%='; }
\|\= { return '|='; }
\&\= { return '&='; }
\^\= { return '^='; }
\= { return '='; }
\+ { return '+'; }
\- { return '-'; }
\* { return '*'; }
\/ { return '/'; }
\% { return '%'; }
\^ { return '^'; }
\& { return '&'; }
\| { return '|'; }
\! { return '!'; }
\< { return '<'; }
\> { return '>'; }
\! { return '!'; }
\? { return '?'; }
\: { return ':'; }
\( { return '('; }
\) { return ')'; }
\[ { return '['; }
\] { return ']'; }
\{ { return '{'; }
\} { return '}'; }
\; { return ';'; }
\, { return ','; }
\. { return '.'; }
<<EOF>> { return 'EOF'; }
. { console.log("INVALID: " + yytext); return 'INVALID'}
/lex
%left ';'
%right 'if' 'else'
%left EMPTY
%left IDLIST
%left ','
%right '?' ':' TERCOND '=' '+=' '-=' '*=' '/=' '%=' '>>=' '<<=' '&=' '|=' '^=' '<==' '==>' '===' '<--' '-->'
%left '||'
%left '&&'
%left '|'
%left '^'
%left '&'
%left '==' '!='
%left '<=' '>=' '<' '>'
%left '<<' '>>'
%left '+' '-'
%left '*' '/' '%'
%left '**'
%right '++' '--' UMINUS UPLUS '!' '~'
%left '.'
%left DECL
%left PLUSPLUSRIGHT MINUSMINUSRIGHT '[' ']' '(' ')'
%left HIGH
%{
const bigInt = require('big-integer');
const util = require('util');
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const __MASK__ = new bigInt(2).pow(253).minus(1);
function setLines(dst, first, last) {
last = last || first;
dst.first_line = first.first_line;
dst.first_column = first.first_column;
dst.last_line = last.last_line;
dst.last_column = last.last_column;
}
%}
%start allStatments
%% /* language grammar */
allStatments
: statmentList EOF
{
// console.log(JSON.stringify($1, null, 1));
$$ = { type: "BLOCK", statements: $1.statments };
setLines($$, @1);
return $$
}
;
statmentList
: statmentList statment
{
$1.statments.push($2);
setLines($1, @1, @2);
}
| statment
{
$$ = { type: "STATMENTLIST", statments: [$1] };
setLines($$, @1);
}
;
statment
: functionDefinitionStatment
{
$$ = $1;
}
| templateDefinitionStatment
{
$$ = $1;
}
| ifStatment
{
$$ = $1;
}
| forStatment
{
$$ = $1;
}
| whileStatment
{
$$ = $1;
}
| doWhileStatment
{
$$ = $1;
}
| returnStatment
{
$$ = $1;
}
| block
{
$$ = $1;
}
| expressionStatment
{
$$ = $1;
}
| includeStatment
{
$$ = $1;
}
;
functionDefinitionStatment
: 'function' IDENTIFIER '(' identifierList ')' block
{
$$ = { type: "FUNCTIONDEF", name: $2, params: $4.identifiers, block: $6};
setLines($$, @1, @6);
}
| 'function' IDENTIFIER '(' ')' block
{
$$ = { type: "FUNCTIONDEF", name: $2, params: [], block: $5 };
setLines($$, @1, @5);
}
;
templateDefinitionStatment
: 'template' IDENTIFIER '(' identifierList ')' block
{
$$ = { type: "TEMPLATEDEF", name: $2, params: $4.identifiers, block: $6 };
setLines($$, @1, @6);
}
| 'template' IDENTIFIER '(' ')' block
{
$$ = { type: "TEMPLATEDEF", name: $2, params: [], block: $5 };
setLines($$, @1, @5);
}
;
identifierList
: identifierList ',' IDENTIFIER
{
$1.identifiers.push($3);
setLines($1, @1, @3);
}
| IDENTIFIER %prec EMPTY
{
$$ = { type: "IDENTIFIERLIST", identifiers: [$1] };
setLines($$, @1);
}
;
ifStatment
: 'if' '(' expression ')' statment 'else' statment
{
if ($3.type == "NUMBER") {
$$ = !$3.value.eq(0) ? $5 : $7;
} else {
$$ = { type: "IF", condition: $3, then: $5, else: $7 };
}
setLines($$, @1, @7);
}
| 'if' '(' expression ')' statment
{
if ($3.type == "NUMBER") {
$$ = !$3.value.eq(0) ? $5 : { type: "NUMBER", value: bigInt(0) };
} else {
$$ = { type: "IF", condition: $3, then: $5 };
}
setLines($$, @1, @5);
}
;
forStatment
: 'for' '(' expression ';' expression ';' expression ')' statment
{
$$ = { type: "FOR", init: $3, condition: $5, step: $7, body: $9 };
setLines($$, @1, @9);
}
;
whileStatment
: 'while' '(' expression ')' statment
{
$$ = { type: "WHILE", condition: $3, body: $5 };
setLines($$, @1, @5);
}
;
doWhileStatment
: 'do' statment 'while' '(' expression ')'
{
$$ = { type: "DOWHILE", condition: $5, body: $2 };
setLines($$, @1, @6);
}
;
returnStatment
: 'return' expression ';'
{
$$ = { type: "RETURN", value: $2 };
setLines($$, @1, @3);
}
| 'return' expression %prec ';'
{
$$ = { type: "RETURN", value: $2 }
setLines($$, @1, @2);
}
;
includeStatment
: 'include' STRING ';'
{
$$ = { type: "INCLUDE", file: $2 };
setLines($$, @1, @3);
}
| 'include' STRING %prec ';'
{
$$ = { type: "INCLUDE", file: $2 }
setLines($$, @1, @2);
}
;
block
: '{' statmentList '}'
{
$$ = { type: "BLOCK", statements: $2.statments };
setLines($$, @1, @3);
}
;
expressionStatment
: expression ';' %prec ';'
{
$$ = $1;
}
| expression %prec ';'
{
$$ = $1;
}
;
expression
: e17 %prec EMPTY
{
$$ = $1;
}
;
e17
: leftHandExpression '=' e17
{
$$ = { type: "OP", op: "=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '+=' e17
{
$$ = { type: "OP", op: "+=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '-=' e17
{
$$ = { type: "OP", op: "-=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '*=' e17
{
$$ = { type: "OP", op: "*=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '/=' e17
{
$$ = { type: "OP", op: "/=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '%=' e17
{
$$ = { type: "OP", op: "%=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '<<=' e17
{
$$ = { type: "OP", op: "<<=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '>>=' e17
{
$$ = { type: "OP", op: ">>=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '&=' e17
{
$$ = { type: "OP", op: "&=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '|=' e17
{
$$ = { type: "OP", op: "|=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '^=' e17
{
$$ = { type: "OP", op: "^=", values: [$1, $3] };
setLines($$, @1, @3);
}
| leftHandExpression '<==' e17
{
$$ = { type: "OP", op: "<==", values: [$1, $3] };
setLines($$, @1, @3);
}
| e17 '==>' leftHandExpression
{
$$ = { type: "OP", op: "<==", values: [$3, $1] };
setLines($$, @1, @3);
}
| leftHandExpression '<--' e17
{
$$ = { type: "OP", op: "<--", values: [$1, $3] };
setLines($$, @1, @3);
}
| e17 '-->' leftHandExpression
{
$$ = { type: "OP", op: "<--", values: [$3, $1] };
setLines($$, @1, @3);
}
| e16 '===' e17
{
$$ = { type: "OP", op: "===", values: [$1, $3] };
setLines($$, @1, @3);
}
| e17 '?' e17 ':' e17 %prec TERCOND
{
if ($1.type == "NUMBER") {
$$ = !$1.value.eq(0) ? $3 : $5;
} else {
$$ = { type: "OP", op: "?", values: [$1, $3, $5] };
}
setLines($$, @1, @5);
}
| e16 %prec EMPTY
{
$$ = $1;
}
;
e16
: rightArray
{
$$ = $1;
}
| e15 %prec EMPTY
{
$$ = $1;
}
;
e15
: e15 '||' e14
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: !$1.value.eq(0) || !$3.value.eq(0) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: "||", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e14 %prec EMPTY
{
$$ = $1;
}
;
e14
: e14 '&&' e13
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: !$1.value.eq(0) && !$3.value.eq(0) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: "&&", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e13 %prec EMPTY
{
$$ = $1;
}
;
e13
: e13 '|' e12
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.or($3.value).and(__MASK__) };
} else {
$$ = { type: "OP", op: "|", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e12 %prec EMPTY
{
$$ = $1;
}
;
e12
: e12 '^' e11
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.or($3.value).and(__MASK__) };
} else {
$$ = { type: "OP", op: "^", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e11 %prec EMPTY
{
$$ = $1;
}
;
e11
: e11 '&' e10
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.and($3.value).and(__MASK__) };
} else {
$$ = { type: "OP", op: "&", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e10 %prec EMPTY
{
$$ = $1;
}
;
e10
: e10 '==' e9
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.equals($3.value) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: "==", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e10 '!=' e9
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.eq($3.value) ? bigInt(0) : bigInt(1) };
} else {
$$ = { type: "OP", op: "!=", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e9 %prec EMPTY
{
$$ = $1
}
;
e9
: e9 '<=' e7
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.lesserOrEquals($3.value) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: "<=", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e9 '>=' e7
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.greaterOrEquals($3.value) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: ">=", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e9 '<' e7
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.lesser($3.value) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: "<", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e9 '>' e7
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.greater($3.value) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: ">", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e7 %prec EMPTY
{
$$ = $1
}
;
e7
: e7 '<<' e6
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
let v = $3.value.greater(256) ? 256 : $3.value.value;
$$ = { type: "NUMBER", value: $1.value.shiftLeft(v).and(__MASK__) };
} else {
$$ = { type: "OP", op: "<<", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e7 '>>' e6
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
let v = $3.value.greater(256) ? 256 : $3.value.value;
$$ = {t1ype: "NUMBER", value: $1.value.shiftRight(v).and(__MASK__) };
} else {
$$ = { type: "OP", op: ">>", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e6 %prec EMPTY
{
$$ = $1;
}
;
e6
: e6 '+' e5
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: ($1.value.plus($3.value)).mod(__P__) };
} else {
$$ = { type: "OP", op: "+", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e6 '-' e5
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: ($1.value.plus(__P__).minus($3.value)).mod(__P__) };
} else {
$$ = { type: "OP", op: "-", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e5 %prec EMPTY
{
$$ = $1;
}
;
e5
: e5 '*' e4
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: ($1.value.times($3.value)).mod(__P__) };
} else {
$$ = { type: "OP", op: "*", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e5 '/' e4
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: ($1.value.times($3.value.modInv(__P__))).mod(__P__) };
} else {
$$ = { type: "OP", op: "/", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e5 '%' e4
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.mod($3.value) };
} else {
$$ = { type: "OP", op: "%", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e4 %prec EMPTY
{
$$ = $1;
}
;
e4
: e4 '**' e3
{
if (($1.type == "NUMBER") && ($3.type == "NUMBER")) {
$$ = { type: "NUMBER", value: $1.value.modPow($3.value, __P__) };
} else {
$$ = { type: "OP", op: "**", values: [$1, $3] };
}
setLines($$, @1, @3);
}
| e3 %prec EMPTY
{
$$ = $1;
}
;
e3
: '++' leftHandExpression
{
$$ = { type: "OP", op: "PLUSPLUSLEFT", values: [$2] };
setLines($$, @1, @2);
}
| '--' leftHandExpression
{
$$ = { type: "OP", op: "MINUSMINUSLEFT", values: [$2] };
setLines($$, @1, @2);
}
| '+' e3 %prec UPLUS
{
$$ = $2;
setLines($$, @1, @2);
}
| '-' e3 %prec UMINUS
{
if ($2.type == "NUMBER") {
$$ = { type: "NUMBER", value: __P__.minus($2.value).mod(__P__) };
} else {
$$ = { type: "OP", op: "UMINUS", values: [$2] };
}
setLines($$, @1, @2);
}
| '!' e3
{
if ($2.type == "NUMBER") {
$$ = { type: "NUMBER", value: $2.value.eq(0) ? bigInt(1) : bigInt(0) };
} else {
$$ = { type: "OP", op: "!", values: [$2] };
}
setLines($$, @1, @2);
}
| '~' e3
{
if ($2.type == "NUMBER") {
$$ = { type: "NUMBER", value: $2.value.xor(__MASK__) };
} else {
$$ = { type: "OP", op: "~", values: [$2] };
}
setLines($$, @1, @2);
}
| e2 %prec EMPTY
{
$$ = $1;
}
;
e2
: leftHandExpression '++' %prec PLUSPLUSRIGHT
{
$$ = {type: "OP", op: "PLUSPLUSRIGHT", values: [$1] };
setLines($$, @1, @2);
}
| leftHandExpression '--' %prec MINUSMINUSRIGHT
{
$$ = {type: "OP", op: "MINUSMINUSRIGHT", values: [$1] };
setLines($$, @1, @2);
}
| functionCall
{
$$ = $1;
}
| e0 %prec EMPTY
{
$$ = $1;
}
;
e0
: leftHandExpression %prec EMPTY
{
$$ = $1
}
| DECNUMBER
{
$$ = {type: "NUMBER", value: bigInt($1).mod(__P__) }
setLines($$, @1);
}
| HEXNUMBER
{
$$ = {type: "NUMBER", value: bigInt($1.substr(2).toUpperCase(), 16).mod(__P__) }
setLines($$, @1);
}
| '(' expression ')' %prec EMPTY
{
$$ = $2;
setLines($$, @1, @3);
}
;
leftHandExpression
: simpleLeftHandExpression '.' simpleLeftHandExpression %prec EMPTY
{
$$ = {type: "PIN", component: $1, pin: $3 };
setLines($$, @1, @3);
}
| declaration %prec DECL
{
$$ = $1
}
| simpleLeftHandExpression %prec EMPTY
{
$$ = $1
}
;
declaration
: 'var' simpleLeftHandExpression %prec DECL
{
$$ = {type: "DECLARE", declareType: "VARIABLE", name: $2}
setLines($$, @1, @2);
}
| 'signal' simpleLeftHandExpression %prec DECL
{
$$ = {type: "DECLARE", declareType: "SIGNAL", name: $2}
setLines($$, @1, @2);
}
| 'signal' 'input' simpleLeftHandExpression %prec DECL
{
$$ = {type: "DECLARE", declareType: "SIGNALIN", name: $3};
setLines($$, @1, @3);
}
| 'signal' 'private' 'input' simpleLeftHandExpression %prec DECL
{
$$ = {type: "DECLARE", declareType: "SIGNALIN", private: true, name: $4};
setLines($$, @1, @4);
}
| 'signal' 'output' simpleLeftHandExpression %prec DECL
{
$$ = {type: "DECLARE", declareType: "SIGNALOUT", name: $3};
setLines($$, @1, @3);
}
| 'component' simpleLeftHandExpression %prec DECL
{
$$ = {type: "DECLARE", declareType: "COMPONENT", name: $2}
setLines($$, @1, @2);
}
;
simpleLeftHandExpression
: simpleLeftHandExpression array
{
for (let i=0; i< $2.values.length; i++) {
$1.selectors.push($2.values[i]);
}
setLines($1, @1, @2);
}
| IDENTIFIER %prec EMPTY
{
$$ = {type: "VARIABLE", name: $1 , selectors: []};
setLines($$, @1);
}
;
functionCall
: IDENTIFIER '(' expressionList ')'
{
$$ = {type: "FUNCTIONCALL", name: $1, params: $3.expressions}
setLines($$, @1, @4);
}
| IDENTIFIER '(' ')'
{
$$ = {type: "FUNCTIONCALL", name: $1, params: []}
setLines($$, @1, @3);
}
;
expressionList
: expressionList ',' expression
{
$1.expressions.push($3);
setLines($$, @1, @3);
}
| expression %prec ','
{
$$ = {type: "EXPRESSIONLST", expressions: [$1]};
setLines($$, @1);
}
;
rightArray
: array %prec EMPTY
{
$$ = $1;
}
;
array
: '[' expressionList ']'
{
$$ = { type: "ARRAY", values: $2.expressions};
setLines($$, @1, @3);
}
;