yacc: how to "save" code for execution in future? - data-structures

I'm stuck in misunderstanding of how to "save" grammar rule parsed by yacc into abstract syntax tree. Here's a piece of my yacc file:
expression:
expr2 { $$ = $1; }
| expr2 EQ expr2 { $$ = ($1 == $3); }
| expr2 NE expr2 { $$ = ($1 != $3); }
| expr2 LT expr2 { $$ = ($1 < $3); }
| expr2 LE expr2 { $$ = ($1 <= $3); }
| expr2 GT expr2 { $$ = ($1 > $3); }
| expr2 GE expr2 { $$ = ($1 >= $3); }
;
expr2:
expr3 { $$ == $1; }
| expr2 PLUS expr3 { $$ = $1 + $3; }
| expr2 MINUS expr3 { $$ = $1 - $3; }
;
expr3:
expr4 { $$ = $1; }
| expr3 MULT expr4 { $$ = $1 * $3; }
| expr3 DIVIDE expr4 { $$ = $1 / $3; }
;
as you can see all the actions are performed on the fly. I'd like to make something like:
expr2 PLUS expr3 { $$ = save_code_for_addition($1, $3); }
to store this action as a node of abstract syntax tree.
Could someone please kindly explain me what would be the inner representation for such a presaved instruction and how on earth can I later execute it via execute(Statement s), what is Statement data type? how to construct it? I would really appriciate any help, thanks.

I've found a manual completely answering this question:
http://web.eecs.utk.edu/~bvz/teaching/cs461Sp11/notes/parse_tree/

Related

False positive IF condition, in BASH. Testing ((A ||B ) && C)

Please see below example test code that I am getting false positive for:
#!/bin/bash
sub1(){
if [[ (($1 -lt 0) || "$1" == "-0") && ($3 =~ re) ]]
then
echo "Sub1: Condition is true $1 $2 $3 $4"
else
echo "Sub1: Condition is false $1 $2 $3 $4"
fi
}
sub2(){
if [[ (($1 -lt 0) || ("$1" == "-0")) && ($3 -ge 0) ]]
then
echo "Sub2: Condition is true $1 $2 $3 $4"
else
echo "Sub2: Condition is false $1 $2 $3 $4"
fi
}
nz='^-0$'
re='^[1-9]+$'
ne='^-[1-9]+$'
A=-0
B=50
C=-0
D=55
sub1 "$A" "$B" "$C" "$D"
sub2 "$A" "$B" "$C" "$D"
I am trying to pass multiple values of C (ex. 0, -0, 1, -1 etc)
Function Sub1 I am trying to check conditions using regex.
Function Sub2 I am trying to check conditions using arithmetic and string conditions.
None of them gives error but both provides false positive when you test with all possible values of C.
Any idea what's wrong in If statement?

bash: command line arguments as script triggers

In my bash routine I am using two alternative methods to use arguments defined during the script execution and assign them to specific variables used by some other functions within the same script:
# tested and works OK
parse_args() {
MY_SORT_METHOD=1 # sort strings according the number of the ligand
MY_FILT_METHOD=0 # filt data according to all strategies
while (($#)); do
case "$1" in
--sort=*)
IFS== read -r _ val <<<"$1"
case "$val" in 1|2) ;; *)
echo "Wrong value is set: please select either the 1st or 2nd method of sorting"
return 1
esac
MY_SORT_METHOD="$val"
;;
--filt=*)
IFS== read -r _ val <<<"$1"
case "$val" in [0-4]) ;; *)
echo "Wrong value is chosen: please select one of four strategies of filtering (--filt=1,2,3,4). Othervise select all four strategies (--filt=0)"
return 1
esac
MY_FILT_METHOD="$val"
;;
esac
shift
done
}
# this does not work
parse_args2() {
args=$(getopt -o '' -l sort:,filt: -- "$#")
eval "set -- $args"
MY_SORT_METHOD=1
MY_FILT_METHOD=0
set -x
while (($#)); do
case "$1" in
--sort) MY_SORT_METHOD="$2"; shift; ;;
--filt) MY_FILT_METHOD="$2"; shift; ;;
--) shift; break; ;;
esac
shift;
done
}
While parse_args() is working OK, there is some bug in parse_args2(), which does not allow to execute this function in the simular fashion... What should I fix there, assuming that the both functions are called in the bash script just using using
parse_args $*
Also here is another function, which uses 4 different AWK scripts to process the same CSV file in the case if a variable defined in the argument is 0: "${MY_FILT_METHOD}" = "0":
filter_csv () {
for d in "${storage}"/*/; do
dir_name=${d%*/}
dir_name=$(basename "$dir_name")
[ "${MY_FILT_METHOD}" = "0" ] && awk -v min_lines=3 -F ", " 'a < $2 {for(idx=0; idx < i; idx++) {print arr[idx]} print $0; a=int($2); i=0; printed=NR} a > $2 && NR > 1 {arr[i]=$0; i++}END{if(printed <= min_lines) {for(idx = 0; idx <= min_lines - printed; idx++){print arr[idx]}}}' "${d}"/${dir_name}*_proc.csv > "${d}"/${dir_name}_str1.csv
[ "${MY_FILT_METHOD}" = "0" ] && awk -v cut1="$cut1" -v cut2="$cut2" -F ', ' 'NR == 1 {next} FNR==NR {if (max < $2) {max=$2; n=FNR+1} next} FNR <= 2 || (FNR == n && $2 > (cut1 * max)) || $2 > (cut2 * max)' "${d}"/${dir_name}*_proc.csv{,} > "${d}"/${dir_name}_str2.csv
[ "${MY_FILT_METHOD}" = "0" ] && awk -v cut3="$cut3" -v cut4="$cut4" -F ', ' 'NR == 1 {next} FNR==NR {if (maxP < $2) maxP=$2; if (minD=="" || minD > $3) minD=$3; next} FNR <= 2 || ($2 >= (cut3 * maxP) && $3 <= (cut4 * minD))' "${d}"/${dir_name}*_proc.csv{,} > "${d}"/${dir_name}_str3a.csv
[ "${MY_FILT_METHOD}" = "0" ] && awk -v cut3="$cut3" -v cut4="$cut4" -F ', ' 'NR == 1 {next} FNR==NR {if (maxP < $2) maxP=$2; if (minD=="" || minD > $3) minD=$3; next} FNR==2{p=$0} FNR == 1 || ($2 >= (cut3 * maxP) && $3 <= (cut4 * minD)) {++c; print} END {if (c==1) print p}' "${d}"/${dir_name}*_proc.csv{,} > "${d}"/${dir_name}_str3b.csv
done
Would it be possible rather to merge those 4 awk commands into one block activated by [ "${MY_FILT_METHOD}" = "0" ] && ?

Pass condition into a function

I´m trying to pass to a function a condition to be applied in a while loop, and if it´s true increase a variable of that condition.
If that´s possible on bash?. Here an example code
a(){
condition=$1
echo "condition:$condition"
while [[ $condition ]] # --> run the condition
do
num1=num1+1
echo "increase num1:$num1" # num1 is a var of the condition
done
}
b(){
num1=1
num2=3
`a $num1 < $num2`
}
b
You can also use as below;
#! /bin/bash
a(){
condition="(( \$num $2 $3 ))"
num=$1
while eval $condition
do
(( num++ ))
echo "increase num1:$num"
done
}
b(){
num1=1
num2=5
a $num1 '<' $num2
}
b
Instead of eval, I would just pass the name of a function which a can call.
a () {
while "$1"; do
...
done
}
b(){
num1=1
num2=3
c () (( num1 < num2 ))
a c
}
b
(Keep in mind that functions are always global; c is not local to the body of b.)
You could do something like below :
a(){
num1="$1";
num2="$3";
if [ "$2" = "<" ]
then
op="-lt"
elif [ "$2" = ">" ]
then
op="-gt"
else
op="-eq"
fi
condition="$num1 $op $num2"
while eval "[ $condition ]" # --> evaluate the condition
do
((num1++)) #num1=num1+1 is in the original is wrong.
echo "increased num1:$num1"
condition="$num1 $op $num2" #rebuild the condition
done
}
b(){
num1=1
num2=3
a "$num1" '<' "$num2" #quote the params, else '<' stands for redirection
}
b
You can try with:
#!/bin/bash
a(){
condition="expr $1 > /dev/null"
while eval $condition; do
echo "num1=$num1 num2=$num2"
let num1++
done
}
b(){
num1=1
num2=3
a '$num1 \< $num2'
}
b
remember the command expr 1 < 3 produces an error:
$ expr 1 < 3
bash: 3: No such file or directory
this is the reason to write \< instead of <

Bash comparison

I have a variable which stores the output of a command. How do I compare it with a float?
To be more specific I am doing
x=$(tail -n 1 foo| cut -d ' ' -f2)
if (($x < 0)); then ...
where foo is a filename. On doing the above I get the following error
-0.08 < 0 : syntax error: invalid arithmetic operator (error token is "0.08 < 0")
The value I need to compare is -0.08, but the error token is different
What should I do for such comparisons?
bash doesn't support floating point arithmetics.
You can however use bc which is an external program to do arithmetics.
if (( $(bc <<< "$x < 0") )); then
printf "%f is less than 0\n" "$x";
fi
from the man page:
The relational operators are
expr1 < expr2
The result is 1 if expr1 is strictly less than expr2.
expr1 <= expr2
The result is 1 if expr1 is less than or equal to expr2.
expr1 > expr2
The result is 1 if expr1 is strictly greater than expr2.
expr1 >= expr2
The result is 1 if expr1 is greater than or equal to expr2.
expr1 == expr2
The result is 1 if expr1 is equal to expr2.
expr1 != expr2
The result is 1 if expr1 is not equal to expr2.
one can also use awk that also supports floating point arithmetics.
If ksh is available to you, you can use it to write your script instead of Bash since it supports floats. Zsh also supports floats.
#!/usr/bin/ksh
x=$(tail -n 1 foo| cut -d ' ' -f2)
if ((x < 0))
then
echo "less than"
fi

Bash always printing same value regardless of boolean value

Related to SO.
fizzy.sh:
#!/usr/bin/env sh
div3() {
expr $1 % 3 = 0
}
div5() {
expr $1 % 5 = 0
}
fizzy() {
if [ $(div3 $1) ] && [ $(div5 $1) ]; then
expr "FizzBuzz"
elif [ $(div3 $1) ]; then
expr "Fizz"
elif [ $(div5 $1) ]; then
expr "Buzz"
else
expr "$1"
fi
}
echo $(fizzy 1)
echo $(fizzy 2)
echo $(fizzy 3)
Example:
$ ./fizzy.sh
FizzBuzz
FizzBuzz
FizzBuzz
expr $1 % 3 = 0 yields 1 or 0, depending on whether the result of $1 % 3 is zero or not, but if treats 0 as true, not false.
sh-3.2$ if [ 0 ]; then echo ok; fi
ok
So you'd need to compare the output of your function against 1. Something like this:
#!/usr/bin/env sh
div3() {
expr $1 % 3 = 0
}
div5() {
expr $1 % 5 = 0
}
fizzy() {
if [ $(div3 $1) -eq 1 ] && [ $(div5 $1) -eq 1 ]; then
expr "FizzBuzz"
elif [ $(div3 $1) -eq 1 ]; then
expr "Fizz"
elif [ $(div5 $1) -eq 1 ]; then
expr "Buzz"
else
expr "$1"
fi
}
for (( i = 1; i <= 15; i++ ))
do
echo $(fizzy $i)
done
Without the need for div3 or div5 functions.
fizzbuzz() { # eg: fizzbuzz 10
((($1%15==0))&& echo FizzBuzz)||
((($1%5==0))&& echo Buzz)||
((($1%3==0))&& echo Fizz)||
echo $1;
}
Or you could do it all at once
fizzbuzz() { # eg: fizzbuzz
for i in {1..100};
do
((($i%15==0))&& echo FizzBuzz)||
((($i%5==0))&& echo Buzz)||
((($i%3==0))&& echo Fizz)||
echo $i;
done;
}
If your shell is bash, you don't need to call out to expr:
div3() { (( $1 % 3 == 0 )); }
div5() { (( $1 % 5 == 0 )); }
fizzbuzz() {
if div3 $1 && div5 $1; then
echo FizzBuzz
elif div3 $1; then
echo Fizz
elif div5 $1; then
echo Buzz
else
echo
fi
}
for ((n=10; n<=15; n++)); do
printf "%d\t%s\n" $n $(fizzbuzz $n)
done

Resources