#!/bin/ksh # # KornShell BASIC-to-C converter --= BACON =-- # # Peter van Eerten - March 2009. # # # March 15, 2009 (0.1) # - Initial release # # March 16, 2009 (0.2) # - renamed internal naming for FUNCTION to avoid mangling # - fixed READ to allow usage with arrays # - fixed INPUT to allow usage with arrays # - fixed FOR to allow usage with arrays # - added variable check in READLN statement # - added variable check in WRITELN statement # - fixed ASC function for high ASCII # # March 17, 2009 (0.3) # - changed internal housekeeping of numeric variables to long # - changed POKE, added MEMORY to claim memory # - replaced DURING for UNTIL # # March 17, 2009 (0.4) # - arguments to define compiler, string memory usage # # March 18, 2009 (0.5) # - improved parsing of imported functions # # March 18, 2009 (0.6) # - improved getting arguments to BACON using 'getopts' # - improved statement matching routines # # March 19, 2009 (0.7) # - added PUSH and PULL # - READ/DATA can handle float values # - CALL only needed when SUB is defined at end of program # - fixed some small potential bugs # # March 20, 2009 (0.8) # - added GETENV() # # March 22, 2009 (0.9) # - added NULL checks for STRING functions # #----------------------------------------------------------- # GLOBAL INITIALIZATONS #----------------------------------------------------------- # Amount of string functions simultanously used in one line let MAX_DEPTH=16 # Link flags LDFLAGS="-lm" # Find 'grep' GREP=`which grep` if [[ -z $GREP ]] then print "ERROR: 'grep' not found on this system!" exit 1 fi # Find 'tr' TR=`which tr` if [[ -z $TR ]] then print "ERROR: 'tr' not found on this system!" exit 1 fi # Find 'indent' INDENT=`which indent` if [[ -z $INDENT ]] then print "WARNING: 'indent' not found on this system!" print "Generated source code will not be beautified." fi #----------------------------------------------------------- function Handle_Print { typeset FORMAT # If there is FORMAT/format argument if [[ "$1" = +(* FORMAT *) || "$1" = +(* format *) ]] then FORMAT=${1##* FORMAT} if [[ "$FORMAT" = "${1}" ]] then FORMAT=${1##* format} print -r "fprintf(stdout, ${FORMAT%%;*}, ${1%%format *});" >> $CFILE else print -r "fprintf(stdout, ${FORMAT%%;*}, ${1%%FORMAT *});" >> $CFILE fi # If no argument, print newline elif [[ "$1" = "PRINT" || "$1" = "print" ]] then print -r "fprintf(stdout, \"\n\");" >> $CFILE # Cast to double, if line ends with ';' then skip newline elif [[ -z ${1##*;} ]] then print -r "fprintf(stdout, \"%g\", (double)${1%%;*});" >> $CFILE else print -r "fprintf(stdout, \"%g\n\", (double)$1);" >> $CFILE fi } #----------------------------------------------------------- function Handle_Printstr { typeset FORMAT # If there is FORMAT argument if [[ "$1" = +(* FORMAT *) || "$1" = +(* format *) ]] then FORMAT=${1##* FORMAT} if [[ "$FORMAT" = "${1}" ]] then FORMAT=${1##* format} print -r "fprintf(stdout, ${FORMAT%%;*}, ${1%%format *});" >> $CFILE else print -r "fprintf(stdout, ${FORMAT%%;*}, ${1%%FORMAT *});" >> $CFILE fi # If no argument, print newline elif [[ "$1" = "PRINTSTR" || "$1" = "printstr" ]] then print -r "fprintf(stdout, \"\n\");" >> $CFILE # String, if line ends with ';' then skip newline elif [[ -z ${1##*;} ]] then print -r "fprintf(stdout, \"%s\", ${1%%;*});" >> $CFILE else print -r "fprintf(stdout, \"%s\n\", $1);" >> $CFILE fi } #----------------------------------------------------------- function Handle_Input { # Local variables typeset CHECK NL VAR # Check if we have an argument at all if [[ "$1" = "INPUT" ]] then print "ERROR: empty INPUT at line $COUNTER!" exit 1 fi # Get the variablename without surrounding spaces VAR=`print ${1}` # Translate function to C function print "fgets(__b2c__input__buffer, $MAX_STRING, stdin);" >> $CFILE # Check type of var, string? if [[ "${VAR##*$}" != "$1" ]] then CHECK=`$GREP -i "$VAR\[$MAX_STRING\];" $HFILE` if [[ -z $CHECK ]] then print "char $VAR[$MAX_STRING];" >> $HFILE fi # Make sure internal var is copied to var of program print "strncpy($VAR, __b2c__input__buffer, $MAX_STRING);" >> $CFILE # Get rid of the terminating newline print -r "$VAR[strlen($VAR)-1] = '\0';" >> $CFILE # Var is numeric else # Variable may not be array, these should be defined with DECLARE if [[ "$VAR" != +(*\[*\]*) ]] then # Not declared? Assume long CHECK=`$GREP -i " $VAR;" $HFILE` if [[ -z $CHECK ]] then print "long $VAR;" >> $HFILE fi else if [[ -z `$GREP -i " ${VAR%%\[*}\[" $HFILE` ]] then print "ERROR: cannot declare implicit array in INPUT statement at $COUNTER!" exit 1 else CHECK=`$GREP -i " ${VAR%%\[*}" $HFILE` fi fi # Make sure internal var is copied to var of program if [[ "$CHECK" = +(double *) ]] then print "$VAR = atof(__b2c__input__buffer);" >> $CFILE else print "$VAR = atoi(__b2c__input__buffer);" >> $CFILE fi fi } #----------------------------------------------------------- function Handle_For { # Local variables typeset FROM TO TMP VAR # Get the variablename without surrounding spaces VAR=`print ${1%%=*}`; TMP=`print ${1#*=}` # Do we have a STRING var? if [[ "${VAR##*$}" != "$VAR" ]] then print "ERROR: variable in FOR statement at line $COUNTER cannot be string!" exit 1 fi # Check if TO is available if [[ "$TMP" != +(* TO *) ]] then if [[ "$TMP" != +(* to *) ]] then print "ERROR: missing TO in FOR statement at line $COUNTER!" exit 1 fi fi # Get the starting and ending value if [[ "$TMP" = +(* TO *) ]] then FROM=`print ${TMP%% TO *}` TO=`print ${TMP##* TO }` else FROM=`print ${TMP%% to *}` TO=`print ${TMP##* to }` fi # Check if there is a STEP if [[ "$TO" = +(* STEP *) ]] then STEP=${TO##* STEP } TO=${TO%% STEP *} elif [[ "$TO" = +(* step *) ]] then STEP=${TO##* step } TO=${TO%% step *} else STEP=1 fi # Translate function to C function if [[ $STEP -lt 0 ]] then print "for($VAR=$FROM; $VAR >= $TO; $VAR+=$STEP){" >> $CFILE else print "for($VAR=$FROM; $VAR <= $TO; $VAR+=$STEP){" >> $CFILE fi # Variable may not be array, these should be defined with DECLARE if [[ "$VAR" = +(*\[*\]*) ]] then if [[ -z `$GREP -i " ${VAR%%\[*}\[" $HFILE` ]] then print "ERROR: cannot declare implicit array in FOR statement at $COUNTER!" exit 1 fi else # Declare variable if not done yet, assuming integer CHECK=`$GREP -i " $VAR;" $HFILE` if [[ -z $CHECK ]] then print "long $VAR;" >> $HFILE fi fi } #----------------------------------------------------------- function Handle_If { # Check if THEN is available if [[ "$1" != +(* THEN) ]] then if [[ "$1" != +(* then) ]] then print "ERROR: Missing THEN in IF statement at line $COUNTER!" exit 1 fi fi # Translate function to C function print "if(${1% *}){" >> $CFILE } #----------------------------------------------------------- function Handle_Elif { # Check if THEN is available if [[ "$1" != +(* THEN) ]] then if [[ "$1" != +(* then) ]] then print "ERROR: Missing THEN in ELIF statement at line $COUNTER!" exit 1 fi fi # Translate function to C function print "} else if(${1% *}){" >> $CFILE } #----------------------------------------------------------- function Handle_While { # Check if DO is available if [[ "$1" != +(* DO) ]] then if [[ "$1" != +(* do) ]] then print "ERROR: Missing DO in WHILE statement at line $COUNTER!" exit 1 fi fi # Translate function to C function print "while(${1% *}){" >> $CFILE } #----------------------------------------------------------- function Handle_Let { # Local variables typeset VAR CHECK TMP # Check if there is an asignment at all, if not exit if [[ "$1" != +(*=*) ]] then print "ERROR: could not parse line $COUNTER!" exit 1 fi # Get the variablename without surrounding spaces VAR=`print ${1%%=*}`; TMP=${1#*=} # Check if var is string var if [[ "${VAR}" = +(*\$) ]] then CHECK=`$GREP -i " $VAR\[$MAX_STRING\];" $HFILE` if [[ -z $CHECK ]] then print "char $VAR[$MAX_STRING];" >> $HFILE fi # Variable may not be array, these should be defined with DECLARE elif [[ "$VAR" = +(*\[*\]*) ]] then print "ERROR: cannot declare array at $COUNTER - use DECLARE!" exit 1 # Assume Integer else CHECK=`$GREP -i " $VAR;" $HFILE` if [[ -z $CHECK ]] then print "long $VAR;" >> $HFILE fi fi # Do we have a STRING var? if [[ "${VAR}" = +(*\$) ]] then print "__b2c__strncpy($VAR, $TMP, $MAX_STRING);" >> $CFILE else print "$1;" >> $CFILE fi } #----------------------------------------------------------- function Handle_Open { # Local variables typeset FILE MODE HANDLE TMP CHECK # Check if FOR is available if [[ "$1" != +(* FOR *) ]] then if [[ "$1" != +(* for *) ]] then print "ERROR: Missing FOR in OPEN statement at line $COUNTER!" exit 1 fi fi # Check if AS is available if [[ "$1" != +(* AS *) ]] then if [[ "$1" != +(* as *) ]] then print "ERROR: Missing AS in OPEN statement at line $COUNTER!" exit 1 fi fi # Get the file, mode and handle if [[ "$1" = +(* FOR *) ]] then FILE=`print ${1%% FOR *}` TMP=`print ${1##* FOR }` else FILE=`print ${1%% for *}` TMP=`print ${1##* for }` fi if [[ "$1" = +(* AS *) ]] then MODE=`print ${TMP%% AS *}` HANDLE=`print ${TMP##* AS }` else MODE=`print ${TMP%% as *}` HANDLE=`print ${TMP##* as }` fi # Check if var is string var if [[ "${HANDLE}" = +(*\$) ]] then print "ERROR: Variable for OPEN at line $COUNTER cannot be string!" exit 1 fi # Check if variable was declared CHECK=`$GREP -i "$HANDLE;" $HFILE` if [[ -z $CHECK ]] then print "FILE* $HANDLE;" >> $HFILE fi # Convert to C syntax case $MODE in @(READING|reading) ) print "$HANDLE = fopen($FILE, \"r\");" >> $CFILE;; @(WRITING|writing) ) print "$HANDLE = fopen($FILE, \"w\");" >> $CFILE;; @(APPENDING|appending) ) print "$HANDLE = fopen($FILE, \"a\");" >> $CFILE;; @(READWRITE|readwrite) ) print "$HANDLE = fopen($FILE, \"r+\");" >> $CFILE;; esac # Error handling print "if($HANDLE == NULL){fprintf(stderr, \"ERROR: %s in OPEN statement!\\\\n\", strerror(errno)); exit(EXIT_FAILURE);}" >> $CFILE } #----------------------------------------------------------- function Handle_Readln { # Local variables typeset TMP CHECK VAR FROM # Check if FROM is available if [[ "$1" != +(* FROM *) ]] then if [[ "$1" != +(* from *) ]] then print "ERROR: Missing FROM in READLN statement at line $COUNTER!" exit 1 fi fi # Get the variablename without surrounding spaces if [[ "$1" = +(* FROM *) ]] then VAR=`print ${1%% FROM *}` # Get filedescriptor FROM=`print ${1##* FROM }` else VAR=`print ${1%% from *}` # Get filedescriptor FROM=`print ${1##* from }` fi # Check if var is string var if [[ "${VAR}" != +(*\$) ]] then print "ERROR: Variable for READLN at line $COUNTER must be string!" exit 1 fi # Translate function to C function print "fgets(__b2c__input__buffer, $MAX_STRING, $FROM);" >> $CFILE # Check if variable is declared CHECK=`$GREP -i " $VAR\[$MAX_STRING\];" $HFILE` if [[ -z $CHECK ]] then print "char $VAR[$MAX_STRING];" >> $HFILE fi # Make sure internal var is copied to var of program print "strncpy($VAR, __b2c__input__buffer, $MAX_STRING);" >> $CFILE # Make sure to end the string print -r "$VAR[strlen(__b2c__input__buffer)] = '\0';" >> $CFILE } #----------------------------------------------------------- function Handle_Writeln { # Local variables typeset VAR TO # Check if TO is available if [[ "$1" != +(* TO *) ]] then if [[ "$1" != +(* to *) ]] then print "ERROR: Missing TO in WRITELN statement at line $COUNTER!" exit 1 fi fi # Get the variablename without surrounding spaces if [[ "$1" = +(* TO *) ]] then VAR=`print ${1%% TO *}`; # Get filedescriptor TO=`print ${1##* TO }` else VAR=`print ${1%% to *}`; # Get filedescriptor TO=`print ${1##* to }` fi # Check if var is string var if [[ "${VAR}" != +(*\$) && "$VAR" = `print "$VAR" | $TR -d "\042"` ]] then print "ERROR: Variable for WRITELN at line $COUNTER must be string!" exit 1 fi # Translate function to C function print "fprintf($TO, \"%s\\\\n\", $VAR);" >> $CFILE } #----------------------------------------------------------- function Handle_Import { # Local variables typeset TMP SYM LIB CHECK TOKEN typeset -l TYPE # Check if FROM is available if [[ "$1" != +(* FROM *) ]] then if [[ "$1" != +(* from *) ]] then print "ERROR: missing FROM in IMPORT statement at line $COUNTER!" exit 1 fi fi # Get the symbolname without surrounding spaces and doublequotes if [[ "$1" = +(* FROM *) ]] then SYM=`print ${1%% FROM *}`; TMP=${1#* FROM } TOKEN=`print $SYM | $TR -d "\042"` else SYM=`print ${1%% from *}`; TMP=${1#* from } TOKEN=`print $SYM | $TR -d "\042"` fi # Check if TYPE is available if [[ "$1" != +(* TYPE *) ]] then if [[ "$1" != +(* type *) ]] then print "ERROR: missing TYPE in IMPORT statement at line $COUNTER!" exit 1 fi fi # Get library and type if [[ "$1" = +(* TYPE *) ]] then LIB=`print ${TMP%% TYPE *}` TYPE=`print ${TMP##* TYPE }` else LIB=`print ${TMP%% type *}` TYPE=`print ${TMP##* type }` fi # If library is libm or libc, skip dlopen as we're linking with those anyway if [[ $LIB != +(*libc.so*) && $LIB != +(*libm.so*) ]] then # Create name from libname PTR=`print $LIB | $TR -d [:punct:]` # Check if variable was declared CHECK=`$GREP -i "void\* __b2c__dlopen__pointer_$PTR;" $HFILE` if [[ -z $CHECK ]] then print "void* __b2c__dlopen__pointer_$PTR;" >> $HFILE print "__b2c__dlopen__pointer_$PTR = dlopen($LIB, RTLD_GLOBAL|RTLD_LAZY);" >> $CFILE print "if(__b2c__dlopen__pointer_$PTR == NULL) {" >> $CFILE print "fprintf(stderr, \"ERROR: %s\\\\n\", dlerror()); exit(EXIT_FAILURE);}" >> $CFILE fi # Check if token was declared CHECK=`$GREP -i "$TYPE (\*$TOKEN)();" $HFILE` if [[ -z $CHECK ]] then print "$TYPE (*$TOKEN)();" >> $HFILE fi # Translate to C function print "*($TYPE **) (&$TOKEN) = dlsym(__b2c__dlopen__pointer_$PTR, $SYM);" >> $CFILE print "if($TOKEN == NULL) {fprintf(stderr, \"ERROR: could not find symbol '$TOKEN' in library!\\\\n\"); exit(EXIT_FAILURE);}" >> $CFILE # Add link flag if [[ $LDFLAGS != +(*-ldl*) ]] then LDFLAGS="$LDFLAGS -ldl" fi fi # Make symbol known to parser IMPORTED="$TOKEN $IMPORTED" } #----------------------------------------------------------- function Handle_Declare { # Local variables typeset VAR TYPE # Check if TYPE is available if [[ "$1" != +(* TYPE *) ]] then if [[ "$1" != +(* type *) ]] then print "ERROR: Missing TYPE in DECLARE statement at line $COUNTER!" exit 1 fi fi # Get the variablename and type if [[ "$1" = +(* TYPE *) ]] then VAR=`print ${1%% TYPE *}` TYPE=`print ${1##* TYPE }` else VAR=`print ${1%% type *}` TYPE=`print ${1##* type }` fi # Check if variable was already declared CHECK=`$GREP -i " $VAR;" $HFILE` if [[ -n $CHECK ]] then print "ERROR: variable in DECLARE statement at line $COUNTER was defined previously!" exit 1 fi # Translate function to C function print "$TYPE $VAR;" >> $HFILE } #----------------------------------------------------------- function Handle_Read { # Local variables typeset CHECK # Check if we have an argument at all if [[ "$1" = "READ" ]] then print "ERROR: empty READ at line $COUNTER!" exit 1 fi # Check type of var, string? if [[ "${1##*$}" != "$1" ]] then CHECK=`$GREP -i "$1\[$MAX_STRING\];" $HFILE` if [[ -z $CHECK ]] then print "char $1[$MAX_STRING];" >> $HFILE fi # Convert to C print "strncpy(${1#* }, __b2c__stringarray[__b2c__stringarray_ptr], $MAX_STRING);" >> $CFILE print "__b2c__stringarray_ptr++;" >> $CFILE else # Variable may not be array, these should be defined with DECLARE if [[ "$1" != +(*\[*\]*) ]] then # Not declared? Assume long CHECK=`$GREP -i " $1;" $HFILE` if [[ -z $CHECK ]] then print "long $1;" >> $HFILE fi else if [[ -z `$GREP -i " ${1%%\[*}\[" $HFILE` ]] then print "ERROR: cannot declare implicit array in READ statement at $COUNTER!" exit 1 else # See how var was declared CHECK=`$GREP -i " ${1%%\[*}" $HFILE` fi fi # Convert to C if [[ "$CHECK" = +(double *) ]] then print "${1#* } = __b2c__floatarray[__b2c__floatarray_ptr];" >> $CFILE print "__b2c__floatarray_ptr++;" >> $CFILE else print "${1#* } = __b2c__intarray[__b2c__intarray_ptr];" >> $CFILE print "__b2c__intarray_ptr++;" >> $CFILE fi fi } #----------------------------------------------------------- function Handle_Function { # Local variables typeset NAME TYPE # Check if TYPE is available if [[ "$1" != +(* TYPE *) ]] then if [[ "$1" != +(* type *) ]] then print "ERROR: Missing TYPE in FUNCTION statement at line $COUNTER!" exit 1 fi fi # Get the funcname and type if [[ "$1" = +(* TYPE *) ]] then NAME=`print ${1%% TYPE *}` TYPE=`print ${1##* TYPE }` else NAME=`print ${1%% type *}` TYPE=`print ${1##* type }` fi # Switch to header file COPY_CFILE=$CFILE CFILE=$HFILE # Create macro print "#define $NAME __b2c__function__$NAME()" >> $HFILE # Translate function C function print "$TYPE __b2c__function__$NAME(void) {" >> $CFILE } #----------------------------------------------------------- function Handle_Push { # Local variables typeset CHECK # Check if we have an argument at all if [[ "$1" = "PUSH" ]] then print "ERROR: empty PUSH at line $COUNTER!" exit 1 fi CHECK=`$GREP -i " $1;" $HFILE` # Check type of var, string? if [[ "${1##*$}" != "$1" ]] then print "strncpy(__b2c__stringstack[__b2c__stackptr], $1, $MAX_STRING);" >> $CFILE print "__b2c__typestack[__b2c__stackptr] = 1;" >> $CFILE # Check if it is a normal string elif [[ "$1" != `print "$1" | $TR -d "\042"` ]] then print "strncpy(__b2c__stringstack[__b2c__stackptr], $1, $MAX_STRING);" >> $CFILE print "__b2c__typestack[__b2c__stackptr] = 1;" >> $CFILE # Check if float elif [[ "$1" = +(*.*) ]] then print "__b2c__doublestack[__b2c__stackptr] = $1;" >> $CFILE print "__b2c__typestack[__b2c__stackptr] = 2;" >> $CFILE # Check if no alpha chars (then integer value) elif [[ "$1" = +(![a-zA-Z]) ]] then print "__b2c__longstack[__b2c__stackptr] = $1;" >> $CFILE print "__b2c__typestack[__b2c__stackptr] = 3;" >> $CFILE # Assume variable, check if declared before elif [[ -n $CHECK ]] then if [[ $CHECK = +(double*) ]] then print "__b2c__doublestack[__b2c__stackptr] = $1;" >> $CFILE print "__b2c__typestack[__b2c__stackptr] = 2;" >> $CFILE else print "__b2c__longstack[__b2c__stackptr] = $1;" >> $CFILE print "__b2c__typestack[__b2c__stackptr] = 3;" >> $CFILE fi # Not declared, assume integer variable else print "__b2c__longstack[__b2c__stackptr] = $1;" >> $CFILE print "__b2c__typestack[__b2c__stackptr] = 3;" >> $CFILE fi # Increase stackpointer print "__b2c__stackptr++;" >> $CFILE print "if(__b2c__stackptr == $STACK_DEPTH) __b2c__stackptr=$STACK_DEPTH-1;" >> $CFILE } #----------------------------------------------------------- function Handle_Pull { # Check if we have an argument at all if [[ "$1" = "PULL" ]] then print "ERROR: empty PULL at line $COUNTER!" exit 1 fi # Argument must be a variable if [[ "$1" = +(![a-zA-Z]) ]] then print "ERROR: argument in PULL statement at line $COUNTER is not a variable!" exit 1 fi # Decrease stackpointer again print "__b2c__stackptr--;" >> $CFILE print "if(__b2c__stackptr < 0) __b2c__stackptr=0;" >> $CFILE # Get the last value from stack if [[ "${1##*$}" != "$1" ]] then CHECK=`$GREP -i "$1\[$MAX_STRING\];" $HFILE` if [[ -z $CHECK ]] then print "char $1[$MAX_STRING];" >> $HFILE fi print "if(__b2c__typestack[__b2c__stackptr] == 1) strncpy($1, __b2c__stringstack[__b2c__stackptr], $MAX_STRING);" >> $CFILE else # Variable may not be array, these should be defined with DECLARE if [[ "$1" != +(*\[*\]*) ]] then # Not declared? Assume long CHECK=`$GREP -i " $1;" $HFILE` if [[ -z $CHECK ]] then print "long $1;" >> $HFILE fi else if [[ -z `$GREP -i " ${1%%\[*}\[" $HFILE` ]] then print "ERROR: cannot declare implicit array in INPUT statement at $COUNTER!" exit 1 else # See how var was declared CHECK=`$GREP -i " ${1%%\[*}" $HFILE` fi fi # Make sure internal var is copied to var of program if [[ "$CHECK" = +(double *) ]] then print "if(__b2c__typestack[__b2c__stackptr] == 2) $1=__b2c__doublestack[__b2c__stackptr];" >> $CFILE else print "if(__b2c__typestack[__b2c__stackptr] == 3) $1=__b2c__longstack[__b2c__stackptr];" >> $CFILE fi fi } #----------------------------------------------------------- # # Simple parser to tokenize line. # # Each line should begin with a statement. # The rest of the line may contain functions, these are # converted using C macros. # #----------------------------------------------------------- function Parse_Line { typeset FOUND SYM INC typeset -u STATEMENT # Get statement without spaces STATEMENT=`print "${1%% *}"` case "$STATEMENT" in "PRINT") Handle_Print "${1#* }";; "PRINTSTR") Handle_Printstr "${1#* }";; "INPUT") Handle_Input "${1#* }";; "FOR") Handle_For "${1#* }";; "NEXT") print "}" >> $CFILE;; "IF") Handle_If "${1#* }";; "ELIF") Handle_Elif "${1#* }";; "ELSE") print "} else {" >> $CFILE;; "ENDIF") print "}" >> $CFILE;; "WHILE") Handle_While "${1#* }";; "WEND") print "}" >> $CFILE;; "BREAK") print "break;" >> $CFILE;; "REPEAT") print "do{" >> $CFILE;; "UNTIL") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty AS at line $COUNTER!" exit 1 else print "} while(!(${1#* }));" >> $CFILE fi;; "LET") Handle_Let "${1#* }";; "REM") ;; "SYSTEM") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty SYSTEM at line $COUNTER!" exit 1 else print "SYSTEM (${1#* });" >> $CFILE fi;; "SLEEP") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty SLEEP at line $COUNTER!" exit 1 else print "SLEEP(${1#* });" >> $CFILE fi;; "OPEN") Handle_Open "${1#* }";; "CLOSE") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty CLOSE at line $COUNTER!" exit 1 else print "CLOSE(${1#* });" >> $CFILE fi;; "REWIND") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty REWIND at line $COUNTER!" exit 1 else print "REWIND(${1#* });" >> $CFILE fi;; "READLN") Handle_Readln "${1#* }";; "WRITELN") Handle_Writeln "${1#* }";; "END") print "END;" >> $CFILE;; "SUB") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty SUB at line $COUNTER!" exit 1 else COPY_CFILE=$CFILE CFILE=$HFILE print "void ${1#* }(void) {" >> $CFILE # Make symbol known to parser IMPORTED="${1#* } $IMPORTED" fi;; "ENDSUB") print "}" >> $CFILE CFILE=$COPY_CFILE;; "CALL") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty CALL at line $COUNTER!" exit 1 else print "${1#* }();" >> $CFILE fi;; "FUNCTION") Handle_Function "${1#* }";; "ENDFUNCTION") print "}" >> $CFILE CFILE=$COPY_CFILE;; "RETURN") print "return (${1#* });" >> $CFILE;; "IMPORT") Handle_Import "${1#* }";; "DECLARE") Handle_Declare "${1#* }";; "DATA") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty DATA at line $COUNTER!" exit 1 else if [[ "${1#* }" != `print "${1#* }" | $TR -d "\042"` ]] then print -r "${1#* }, " >> $STRINGARRAYFILE else # Check if float if [[ "${1#* }" = +(*.*) ]] then print -r "${1#* }, " >> $FLOATARRAYFILE else print -r "${1#* }, " >> $INTARRAYFILE fi fi fi;; "RESTORE") print "if(__b2c__intarray_ptr > 0) __b2c__intarray_ptr = 0;" >> $CFILE print "if(__b2c__floatarray_ptr > 0) __b2c__floatarray_ptr = 0;" >> $CFILE print "if(__b2c__stringarray_ptr > 0) __b2c__stringarray_ptr = 0;" >> $CFILE;; "READ") Handle_Read "${1#* }";; "PUSH") Handle_Push "${1#* }";; "PULL") Handle_Pull "${1#* }";; "INCLUDE") # Get rid of doublequotes if they are there INC=`print ${1#* } | $TR -d "\042"` # Check argument if [[ ! -f $INC || "${1%% *}" = "${1#* }" ]] then print "ERROR: missing file for INCLUDE at line $COUNTER!" exit 1 else # Start walking through program COPY_COUNTER=$COUNTER COUNTER=1 while read -r LINE do # Line is not empty? if [[ -n "$LINE" ]] then Parse_Line "$LINE" fi ((COUNTER+=1)) done < $INC # Restore original counter COUNTER=$COPY_COUNTER fi;; "POKE") # Check argument if [[ "${1%% *}" = "${1#* }" ]] then print "ERROR: empty POKE at line $COUNTER!" exit 1 else # Resolve this with C macro print "POKE(${1#* });" >> $CFILE fi;; *) # Check on imported symbols first FOUND=0 for SYM in $IMPORTED do if [[ "$1" = +($SYM*) ]] then FOUND=1 if [[ "$1" != ${1#*\(} ]] then print -r "($SYM)(${1#*\(};" >> $CFILE else print -r "($SYM)();" >> $CFILE fi fi done # Not an imported symbol? Check if assignment if [[ $FOUND -eq 0 ]] then Handle_Let "$1" fi;; esac } #----------------------------------------------------------- # # Main program # #----------------------------------------------------------- # Default BACON settings let MAX_STRING=256 let NO_COMPILE=0 let STACK_DEPTH=8 CCNAME=gcc CCFLAGS= # Get arguments while getopts ":m:c:s:gn" OPT do case $OPT in m) MAX_STRING=$OPTARG;; c) CCNAME=$OPTARG;; s) STACK_DEPTH=$OPTARG;; n) NO_COMPILE=1;; g) CCFLAGS="-g";; \?) print print "USAGE: bacon [options] program[.bac]" print print "OPTIONS:" print print " -m \tMax string length (default: 256)" print " -c \tCompiler to use (default: gcc)" print " -s \tDepth of stack (default: 8)" print " -n \t\tDo not compile automatically" print " -g \t\tCompile with debug info" print exit 1;; esac done shift $(($OPTIND-1)) # Check if a filename was entered, if so get it if [[ $# -eq 0 ]] then print "ERROR: no filename? Run with '-?' to see usage." exit 1 elif [[ "$@" != +(*.bac) ]] then SOURCEFILE="$@.bac" else SOURCEFILE="$@" fi # Check if file exists if [[ ! -f $SOURCEFILE ]] then print "ERROR: file not found!" exit 1 fi print -n "Starting conversion... " # Now create the global filenames where to write to CFILE=${SOURCEFILE%.*}.c HFILE=${SOURCEFILE%.*}.h STRINGARRAYFILE=${SOURCEFILE%.*}.string INTARRAYFILE=${SOURCEFILE%.*}.int FLOATARRAYFILE=${SOURCEFILE%.*}.float # Create basic C file print "/* Created with BACON - PvE 2009 */" > $CFILE print "#include \"$HFILE\"" >> $CFILE print "int main(int argc, const char **argv)" >> $CFILE print "{" >> $CFILE # Put arguments into reserved variable ARGUMENTS print "/* Setup the reserved variable 'ARGUMENTS' */" >> $CFILE print "for(__b2c__counter=1; __b2c__counter < argc; __b2c__counter++)" >> $CFILE print "{strncat(ARGUMENTS, argv[__b2c__counter], $MAX_STRING); if(__b2c__counter != argc - 1) strcat(ARGUMENTS, \" \");" >> $CFILE print "strncat(arguments, argv[__b2c__counter], $MAX_STRING); if(__b2c__counter != argc - 1) strcat(arguments, \" \");}" >> $CFILE print "/* Rest of the program */" >> $CFILE # Create basic H file, functions are converted using macros print "/* Created with BACON - PvE 2009 */" > $HFILE print >> $HFILE print "#include " >> $HFILE print >> $HFILE print "/* Math functions */" >> $HFILE print "#include " >> $HFILE print "#define sqr(x) sqrt(x)" >> $HFILE print "#define SQR(x) sqrt(x)" >> $HFILE print "#define POW(x, y) pow(x, y)" >> $HFILE print "#define SIN(x) sin(x)" >> $HFILE print "#define COS(x) cos(x)" >> $HFILE print "#define TAN(x) tan(x)" >> $HFILE print "#define round(x) lrint(x)" >> $HFILE print "#define ROUND(x) lrint(x)" >> $HFILE print "#define mod(x,y) (x%y)" >> $HFILE print "#define MOD(x,y) (x%y)" >> $HFILE print "#define FLOOR(x) floor(x)" >> $HFILE print >> $HFILE print "/* Other functions */" >> $HFILE print "#include " >> $HFILE print "#define END exit(EXIT_SUCCESS)" >> $HFILE print "#define SYSTEM(x) system(x)" >> $HFILE print "#define val(x) ((x != NULL) ? atoi(x) : 0)" >> $HFILE print "#define VAL(x) ((x != NULL) ? atoi(x) : 0)" >> $HFILE print >> $HFILE print "/* Unix functions */" >> $HFILE print "#include " >> $HFILE print "#define sleep(x) usleep(x*1000)" >> $HFILE print "#define SLEEP(x) usleep(x*1000)" >> $HFILE print >> $HFILE print "/* String functions */" >> $HFILE print "#include " >> $HFILE print "char __b2c__input__buffer[$MAX_STRING];" >> $HFILE print "char __b2c__stringbuffer[$MAX_STRING*$MAX_DEPTH];" >> $HFILE print "int __b2c__stringbuffer_ptr = 0;" >> $HFILE print "char* __b2c__strncpy(char *dest, const char *src, size_t n){" >> $HFILE print "size_t __b2c__i; for (__b2c__i = 0; __b2c__i < n && src[__b2c__i] != '\\\\0'; __b2c__i++) dest[__b2c__i] = src[__b2c__i];" >> $HFILE print "dest[__b2c__i] = '\\\\0'; __b2c__stringbuffer_ptr+=$MAX_STRING;" >> $HFILE print "if(__b2c__stringbuffer_ptr >= $MAX_STRING*$MAX_DEPTH) __b2c__stringbuffer_ptr = 0; return dest;}" >> $HFILE print "char* __b2c__reverse(char *s){int __b2c__i; char *__b2c__tmp; __b2c__tmp = __b2c__stringbuffer + __b2c__stringbuffer_ptr;" >> $HFILE print "for(__b2c__i=0;__b2c__i> $HFILE print "if(__b2c__stringbuffer_ptr >= $MAX_STRING*$MAX_DEPTH) __b2c__stringbuffer_ptr = 0;" >> $HFILE print "return(__b2c__tmp);}" >> $HFILE print "char* __b2c__str(double d){char *__b2c__tmp; __b2c__tmp = __b2c__stringbuffer + __b2c__stringbuffer_ptr;" >> $HFILE print "snprintf(__b2c__tmp, $MAX_STRING, \"%g\", d); __b2c__stringbuffer_ptr+=$MAX_STRING;" >> $HFILE print "if(__b2c__stringbuffer_ptr >= $MAX_STRING*$MAX_DEPTH) __b2c__stringbuffer_ptr = 0;" >> $HFILE print "return(__b2c__tmp);}" >> $HFILE print "char* __b2c__concat(char *__b2c__x, char *__b2c__y){char *__b2c__tmp; char *__b2c__tmq;" >> $HFILE print "if (__b2c__x == NULL && __b2c__y != NULL) return __b2c__y; if (__b2c__x != NULL && __b2c__y == NULL) return __b2c__x;" >> $HFILE print "if (__b2c__x == NULL && __b2c__y == NULL) return (\"null\");" >> $HFILE print "__b2c__tmp = __b2c__stringbuffer + __b2c__stringbuffer_ptr; __b2c__strncpy(__b2c__tmp, __b2c__x, $MAX_STRING);" >> $HFILE print "__b2c__tmq = __b2c__stringbuffer + __b2c__stringbuffer_ptr; __b2c__strncpy(__b2c__tmq, __b2c__y, $MAX_STRING);" >> $HFILE print "return(strncat(__b2c__tmp, __b2c__tmq, $MAX_STRING-1));}" >> $HFILE print "#define left(x, y) ((x != NULL && y < strlen(x) && y >= 0) ? __b2c__strncpy(__b2c__stringbuffer + __b2c__stringbuffer_ptr, x, (y)) : x)" >> $HFILE print "#define LEFT(x, y) ((x != NULL && y < strlen(x) && y >= 0) ? __b2c__strncpy(__b2c__stringbuffer + __b2c__stringbuffer_ptr, x, (y)) : x)" >> $HFILE print "#define right(x, y) ((x != NULL && y < strlen(x) && y >= 0) ? __b2c__strncpy(__b2c__stringbuffer + __b2c__stringbuffer_ptr, x+strlen(x)-(int)(y), (y)) : \"\")" >> $HFILE print "#define RIGHT(x, y) ((x != NULL && y < strlen(x) && y >= 0) ? __b2c__strncpy(__b2c__stringbuffer + __b2c__stringbuffer_ptr, x+strlen(x)-(int)(y), (y)) : \"\")" >> $HFILE print "#define mid(x, y, z) ((x != NULL && y < strlen(x) && y >= 0) ? __b2c__strncpy(__b2c__stringbuffer + __b2c__stringbuffer_ptr, x+(int)(y), (z)) : \"\")" >> $HFILE print "#define MID(x, y, z) ((x != NULL && y < strlen(x) && y >= 0) ? __b2c__strncpy(__b2c__stringbuffer + __b2c__stringbuffer_ptr, x+(int)(y), (z)) : \"\")" >> $HFILE print "#define concat(x, y) __b2c__concat(x, y)" >> $HFILE print "#define CONCAT(x, y) __b2c__concat(x, y)" >> $HFILE print "#define instr(x, y) ((x != NULL && y != NULL && strstr(x, y) != NULL) ? (strstr(x, y) - x) : -1)" >> $HFILE print "#define INSTR(x, y) ((x != NULL && y != NULL && strstr(x, y) != NULL) ? (strstr(x, y) - x) : -1)" >> $HFILE print "#define reverse(x) ((x != NULL) ? __b2c__reverse(x) : \"null\")" >> $HFILE print "#define REVERSE(x) ((x != NULL) ? __b2c__reverse(x) : \"null\")" >> $HFILE print "#define str(x) __b2c__str(x)" >> $HFILE print "#define STR(x) __b2c__str(x)" >> $HFILE print "#define len(x) ((x != NULL) ? strlen(x) : 0)" >> $HFILE print "#define LEN(x) ((x != NULL) ? strlen(x) : 0)" >> $HFILE print "#define equal(x, y) ((x != NULL && y != NULL) ? !strncmp(x, y, $MAX_STRING) : 0)" >> $HFILE print "#define EQUAL(x, y) ((x != NULL && y != NULL) ? !strncmp(x, y, $MAX_STRING) : 0)" >> $HFILE print "#define getenviron(x) ((x != NULL) ? getenv(x) : \"null\")" >> $HFILE print "#define GETENVIRON(x) ((x != NULL) ? getenv(x) : \"null\")" >> $HFILE print >> $HFILE print "/* I/O functions */" >> $HFILE print "char* __b2c__dec2hex(int i){char *tmp; tmp=__b2c__stringbuffer + __b2c__stringbuffer_ptr;" >> $HFILE print "snprintf(tmp, $MAX_STRING, \"%X\", i); __b2c__stringbuffer_ptr+=$MAX_STRING;" >> $HFILE print "if(__b2c__stringbuffer_ptr >= $MAX_STRING*$MAX_DEPTH) __b2c__stringbuffer_ptr = 0;" >> $HFILE print "return(tmp);}" >> $HFILE print "long __b2c__hex2dec(char *h){int __b2c__i; if(sscanf(h, \"%X\", &__b2c__i) == EOF){" >> $HFILE print "fprintf(stderr, \"ERROR: wrong value in DEC!\\\\n\"); exit(EXIT_FAILURE);}; return(long)(__b2c__i);}" >> $HFILE print "unsigned char __b2c__char2asc(char *c){return (unsigned char)*c;}" >> $HFILE print "char* __b2c__asc2char(int i){char *tmp; tmp=__b2c__stringbuffer + __b2c__stringbuffer_ptr;" >> $HFILE print "sprintf(tmp, \"%c\", i); __b2c__stringbuffer_ptr+=$MAX_STRING;" >> $HFILE print "if(__b2c__stringbuffer_ptr >= $MAX_STRING*$MAX_DEPTH) __b2c__stringbuffer_ptr = 0;" >> $HFILE print "return(tmp);}" >> $HFILE print "#define CLOSE(x) fclose(x)" >> $HFILE print "#define REWIND(x) rewind(x)" >> $HFILE print "#define ENDFILE(x) feof(x)" >> $HFILE print "#define hex(x) __b2c__dec2hex(x)" >> $HFILE print "#define HEX(x) __b2c__dec2hex(x)" >> $HFILE print "#define dec(x) __b2c__hex2dec(x)" >> $HFILE print "#define DEC(x) __b2c__hex2dec(x)" >> $HFILE print "#define asc(x) __b2c__char2asc(x)" >> $HFILE print "#define ASC(x) __b2c__char2asc(x)" >> $HFILE print "#define chr(x) ((x < 256 && x >= 0) ? __b2c__asc2char(x) : \"\")" >> $HFILE print "#define CHR(x) ((x < 256 && x >= 0) ? __b2c__asc2char(x) : \"\")" >> $HFILE print >> $HFILE print "/* Dynamic loading, errors */" >> $HFILE print "#include " >> $HFILE print "#include " >> $HFILE print >> $HFILE print "/* GETKEY */" >> $HFILE print "#include " >> $HFILE print "long __b2c__getch(){long ch; struct termios oldt, newt; tcgetattr(0, &oldt);" >> $HFILE print "newt = oldt; newt.c_lflag &= ~(ICANON | ECHO); tcsetattr(0, TCSANOW, &newt);" >> $HFILE print "ch = getchar(); tcsetattr(0, TCSANOW, &oldt); return ch;} " >> $HFILE print "#define GETKEY __b2c__getch()" >> $HFILE print >> $HFILE print "/* Constants, logical stuff */" >> $HFILE print "#define pi 3.14159265" >> $HFILE print "#define PI 3.14159265" >> $HFILE print "#define not(x) !(x)" >> $HFILE print "#define NOT(x) !(x)" >> $HFILE print "#define and &&" >> $HFILE print "#define AND &&" >> $HFILE print "#define or ||" >> $HFILE print "#define OR ||" >> $HFILE print "#define eq ==" >> $HFILE print "#define EQ ==" >> $HFILE print "#define true 1" >> $HFILE print "#define TRUE 1" >> $HFILE print "#define false 0" >> $HFILE print "#define FALSE 0" >> $HFILE print >> $HFILE print "/* Peek and Poke */" >> $HFILE print "#include " >> $HFILE print "void __b2c__poke(char* x, long y){struct stat sb; if (stat(x, &sb) == -1 && errno == EFAULT) {" >> $HFILE print "fprintf(stderr, \"ERROR: Trying to access illegal memory with POKE at line %d!\\\\n\", __LINE__); exit(EXIT_FAILURE);}" >> $HFILE print "memset(x, y, sizeof(char));}" >> $HFILE print "#define poke(x, y) __b2c__poke((char*)x, y)" >> $HFILE print "#define POKE(x, y) __b2c__poke((char*)x, y)" >> $HFILE print "#define memory(x) (long)malloc(sizeof(char)*x);" >> $HFILE print "#define MEMORY(x) (long)malloc(sizeof(char)*x);" >> $HFILE print "long __b2c__peek(char* x) {struct stat sb; if (stat(x, &sb) == -1 && errno == EFAULT) {" >> $HFILE print "fprintf(stderr, \"ERROR: Trying to access illegal memory with PEEK at line %d!\\\\n\", __LINE__); exit(EXIT_FAILURE);} return(*x);}" >> $HFILE print "#define peek(x) __b2c__peek((char*)x)" >> $HFILE print "#define PEEK(x) __b2c__peek((char*)x)" >> $HFILE print >> $HFILE print "/* Declare reserved variable 'ARGUMENTS' */" >> $HFILE print "int __b2c__counter;" >> $HFILE print "char ARGUMENTS[$MAX_STRING];" >> $HFILE print "char arguments[$MAX_STRING];" >> $HFILE print >> $HFILE print "/* Initialize stack arrays and pointer */" >> $HFILE print "char __b2c__stringstack[$STACK_DEPTH][$MAX_STRING];" >> $HFILE print "double __b2c__doublestack[$STACK_DEPTH];" >> $HFILE print "long __b2c__longstack[$STACK_DEPTH];" >> $HFILE print "int __b2c__typestack[$STACK_DEPTH];" >> $HFILE print "int __b2c__stackptr;" >> $HFILE print >> $HFILE print "/* User program definitions */" >> $HFILE # Initialize the arrayfiles print "char __b2c__stringarray[][$MAX_STRING] = {" > $STRINGARRAYFILE print "int __b2c__intarray[] = {" > $INTARRAYFILE print "double __b2c__floatarray[] = {" > $FLOATARRAYFILE # There are no imported symbols yet IMPORTED= # Start walking through program COUNTER=1 while read -r LINE do # Line is not empty? if [[ -n "$LINE" ]] then Parse_Line "$LINE" fi ((COUNTER+=1)) done < $SOURCEFILE # Finalize main C-file print "return 0;" >> $CFILE print "}" >> $CFILE # Finalize INT ARRAY file for DATA print " \"\" };" >> $STRINGARRAYFILE print >> $HFILE print "/* DATA definitions (string) */" >> $HFILE cat $STRINGARRAYFILE >> $HFILE rm $STRINGARRAYFILE print "int __b2c__stringarray_ptr = 0;" >> $HFILE # Finalize STRING ARRAY file for DATA print " 0};" >> $INTARRAYFILE print >> $HFILE print "/* DATA definitions (long) */" >> $HFILE cat $INTARRAYFILE >> $HFILE rm $INTARRAYFILE print "int __b2c__intarray_ptr = 0;" >> $HFILE # Finalize FLOAT ARRAY file for DATA print " 0.0};" >> $FLOATARRAYFILE print >> $HFILE print "/* DATA definitions (double) */" >> $HFILE cat $FLOATARRAYFILE >> $HFILE rm $FLOATARRAYFILE print "int __b2c__floatarray_ptr = 0;" >> $HFILE print "done." # Start indentation if [[ -n $INDENT ]] then print -n "Applying indentation... " $INDENT $CFILE $INDENT $HFILE rm $CFILE~ rm $HFILE~ print "done." fi # Start compilation if [[ $NO_COMPILE -eq 0 ]] then print -n "Starting compilation... " $CCNAME -Wall $CCFLAGS -o ${SOURCEFILE%.*} $CFILE $LDFLAGS > ${SOURCEFILE%.*}.log 2>&1 if [[ -z `cat ${SOURCEFILE%.*}.log` ]] then print "done." print "Program '${SOURCEFILE%.*}' ready." else cat ${SOURCEFILE%.*}.log fi fi exit 0