%{
/* bic.y - parser and smarts for bigint version of bc
**
** Copyright © 2000,2026 by Jef Poskanzer <jef@mail.acme.com>.
** All rights reserved.
**
** You may use this software however you like as long as you keep my
** name on it and don't sue me.
**
** For commentary on this license please see http://acme.com/license.html
*/

#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include "liblmin.h"

#define YYSTYPE lmin_cellidx

int yyparse( void );
void yyerror( char* msg );
extern int yylex( void );

static char* argv0;

int base;
extern int linenum;

YYSTYPE prev_val;

static YYSTYPE vars[26];


static void usage( void )
    {
    fprintf( stderr, "usage:  %s [-h]\n", argv0 );
    exit( 1 );
    }


static void init( void )
    {
    const char* bigint[] = {
#include "bigint.h"
	};
    int i;

    lmin_init();
    lmin_load_addon( "bigint", bigint, sizeof(bigint) / sizeof(*bigint) );
    prev_val = lmin_call( "bi_0", lmin_nil );
    for ( i = 0; i < 26; ++i )
	vars[i] = prev_val;
    }


static void term( void )
    {
    lmin_term();
    }


void yyerror( char* msg )
    {
    if ( ! isatty( fileno( stdin ) ) )
	fprintf( stderr, "line %d: ", linenum );
    fprintf( stderr, "%s\n", msg );
    }


static void prompt( void )
    {
    if ( isatty( fileno( stdin ) ) )
	printf( "> " );
    }


static lmin_cellidx toint( lmin_cellidx b )
    {
    if ( lmin_vector_len_get( b ) > 1 )
	lmin_errex_err_make( "not an int" );
    return lmin_vector_get( b, 0 );
    }


int main( int argc, char** argv )
    {
    int argn;

    argv0 = argv[0];
    base = 10;
    linenum = 1;
    argn = 1;
    while ( argn < argc && argv[argn][0] == '-' )
	{
	if ( strcmp( argv[argn], "-h" ) == 0 )
	    base = 16;
	else
	    usage();
	++argn;
	}
    if ( argn != argc )
	usage();

    srandom( (unsigned int) time( 0 ) ^ (unsigned int) getpid() );

    init();

    prompt();
    yyparse();

    term();
    exit( 0 );
    }

%}

%token NEWLINE
%token NUMBER
%token SQRT
%token GCD
%token LCM
%token MODPOW
%token MODINV
%token RANDOM
%token ISPRIME
%token GENPRIME
%token BITS
%token DIGITS
%token BIT
%token MODMULT
%token SQUARE
%token MODSQUARE
%token VAR

%left '+' '-'
%left '*' '/' '%'
%right '^'
%left '!'
%left NEG

%%

input			: /* empty */
			| input line
			    {
			    prompt();
			    }
			;
line			: NEWLINE
			| expr NEWLINE
			    {
			    if ( lmin_errex_is( $1 ) )
				lmin_show_errex( stdin, "stdin", linenum, $1 );
			    else
				{
				prev_val = $1;
				if ( lmin_vector_is( $1 ) )
				    lmin_call( "bigint-write-base", $1, lmin_num_integer_make( base ), lmin_nil );
				else if ( lmin_nil_is( $1 ) )
				    fputs( "false", stdout );
				else
				    fputs( "true", stdout );
				putchar( '\n' );
				}
			    lmin_gc();
			    }
			| VAR '=' expr NEWLINE
			    {
			    vars[$1] = $3;
			    }
			| error NEWLINE
			    {
			    yyerrok;
			    }
			;
expr			: expr '+' expr
			    { $$ = lmin_call( "bigint-add", $1, $3, lmin_nil ); }
			| expr '-' expr
			    { $$ = lmin_call( "bigint-subtract", $1, $3, lmin_nil ); }
			| '-' expr  %prec NEG
			    { $$ = lmin_call( "bigint-negate", $2, lmin_nil ); }
			| expr '*' expr
			    { $$ = lmin_call( "bigint-multiply", $1, $3, lmin_nil ); }
			| expr '/' expr
			    { $$ = lmin_call( "bigint-divide", $1, $3, lmin_nil ); }
			| expr '%' expr
			    { $$ = lmin_call( "bigint-rem", $1, $3, lmin_nil ); }
			| expr '^' expr
			    { $$ = lmin_call( "bigint-power", $1, $3, lmin_nil ); }
			| expr '!'
			    { $$ = lmin_call( "bigint-factorial", $1, lmin_nil ); }
			| SQRT '(' expr ')'
			    { $$ = lmin_call( "bigint-sqrt", $3, lmin_nil ); }
			| GCD '(' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-gcd", $3, $5, lmin_nil ); }
			| LCM '(' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-lcm", $3, $5, lmin_nil ); }
			| MODPOW '(' expr ',' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-mod-power", $3, $5, $7, lmin_nil ); }
			| MODINV '(' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-mod-inverse", $3, $5, lmin_nil ); }
			| RANDOM '(' expr ')'
			    { $$ = lmin_call( "bigint-random", $3, lmin_nil ); }
			| ISPRIME '(' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-is-probable-prime?", $3, toint( $5 ), lmin_nil ); }
			| GENPRIME '(' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-generate-prime", toint( $3 ), toint( $5 ), lmin_nil ); }
			| BITS '(' expr ')'
			    { $$ = lmin_call( "bigint", lmin_call( "bigint-bits", $3, lmin_nil ), lmin_nil ); }
			| DIGITS '(' expr ')'
			    { $$ = lmin_call( "bigint", lmin_call( "bigint-digits", $3, lmin_nil ), lmin_nil ); }
			| BIT '(' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-bit", $3, $5, lmin_nil ); }
			| MODMULT '(' expr ',' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-mod-multiply", $3, $5, $7, lmin_nil ); }
			| SQUARE '(' expr ')'
			    { $$ = lmin_call( "bigint-square", $3, lmin_nil ); }
			| MODSQUARE '(' expr ',' expr ')'
			    { $$ = lmin_call( "bigint-mod-square", $3, $5, lmin_nil ); }
			| '(' expr ')'
			    { $$ = $2; }
			| NUMBER
			    { $$ = $1; }
			| VAR
			    { $$ = vars[$1]; }
			;
%%
