How to generate bytecode for if-else statement - if-statement

how do I generate the code corresponding to a bytecode instruction IF THEN - ELSE with the optioanal ELSE branch?
For example, the program If-else.pas is considered correct, while the program If.pas isn't considered correct, because it doesn't contains the ELSE branch.
If-else.pas
var a, b : integer;
begin
a := 3;
b := 5;
if a > b then
print(a)
else
print(b)
end
If.pas
var a, b : integer;
begin
a := 3;
b := 5;
if a > b then
print(a)
end
So Jasmin give me this error:
Output.j:62: JAS Error: Label: L11 has not been added to the code.
Output.j: Found 1 errors
My grammar .g has this rule:
stmt -> ID := expr
| print( expr )
| if( expr ) then ( stmt ) [ else stmt ]?
| while( expr ) do stmt
| begin stmt [ ; stmt ]* end
For the if-else statement I wrote this:
'if'
{
int lfalse = code.newLabel(); //Generates a new number for the LABEL
int lnext = lfalse;
}
( expr )
{
if($expr.type != Type.BOOLEAN) //Checking the condition is boolean
throw new IllegalArgumentException("Type error in '( expr )': expr is not a boolean.");
code.emit(Opcode.IFEQ, lfalse); //I create the instruction IFEQ L(lfalse)
}
'then' s1 = stmt
{
lnext = code.newLabel(); //Generates a new number for the LABEL
code.emit(Opcode.GOTO, lnext); //I create the instruction GOTO L(lnext)
code.emit(Opcode.LABEL, lfalse); //I create the instruction L(lfalse):
}
( 'else' s2 = stmt
{
code.emit(Opcode.LABEL, lnext); //I create the instruction L(lnext):
})?
But in this way the second branch is not optional, but must always be present.
How do I make it optional? I thought it was necessary the question mark (( 'else' s2 = stmt )?), but no.
I am using ANTLR.
Thanks.
I do not know if the bytecode files (. J) generated by Jasmin will be useful, but I write it.
If-else.j
ldc 3
istore 1
ldc 5
istore 0
iload 1
iload 0
if_icmpgt L7
ldc 0
goto L8
L7:
ldc 1
L8:
ifeq L4
iload 1
invokestatic Output/printInt(I)V
goto L11
L4:
iload 0
invokestatic Output/printInt(I)V
L11:
return
If.j
ldc 3
istore 1
ldc 5
istore 0
iload 1
iload 0
if_icmpgt L7
ldc 0
goto L8
L7:
ldc 1
L8:
ifeq L4
iload 1
invokestatic Output/printInt(I)V
goto L11
L4:
return

The problem here is that you're always generating a jump to LNEXT, but you don't generate the label itself when there's no else clause, leading to invalid code. You need to generate the label unconditionally.
I'm not familiar with Antlr, but based on the way your code is written, I suspect this is the correct way to do it.
'if'
{
int lfalse = code.newLabel(); //Generates a new number for the LABEL
int lnext = lfalse;
}
( expr )
{
if($expr.type != Type.BOOLEAN) //Checking the condition is boolean
throw new IllegalArgumentException("Type error in '( expr )': expr is not a boolean.");
code.emit(Opcode.IFEQ, lfalse); //I create the instruction IFEQ L(lfalse)
}
'then' s1 = stmt
{
lnext = code.newLabel(); //Generates a new number for the LABEL
code.emit(Opcode.GOTO, lnext); //I create the instruction GOTO L(lnext)
code.emit(Opcode.LABEL, lfalse); //I create the instruction L(lfalse):
}
( 'else' s2 = stmt )?
{
code.emit(Opcode.LABEL, lnext); //I create the instruction L(lnext):
}

Related

How to return value of if else statement in ocaml?

I'd like to return the value of an if else statement in ocaml.
For example, if I do
let myvalue = if my_condition != 0 then do_this else do_this_instead
But this doesn't seem to store the result of do_this or do_this_instead in myvalue. If I was doing this in C
if(my_condition)
{
return 1 + 1;
} else {
return 1 - 1;
}
I want to kind of achieve the same affect, where not only is the if-else logic executed, but there is also kind of like a return value associated with the executed statement that is caught and stored. How might I do this in ocaml?
If I do following...
# let myvalue = if 2 > 1 then 2 else 1;;
val myvalue : int = 2
# myvalue;;
- : int = 2
What else are you expecting?
if-then-else is an expression construct, and expressions produce/have values.
Or else, if you looking to call other functions for values in the corresponding then and else construct, we can try following way...
# let my_condition = 2 in
let do_this () = 1 + 1 in
let do_this_instead () = 1 - 1 in
myvalue = if my_condition != 0 then do_this () else do_this_instead ();;
- : bool = true
# myvalue;;
- : int = 2

How can I get arround Ocaml's type error?

I have this warning and I would like to know why he wait an expression of type Unix.file_descr list * Unix.file_descr list * Unix.file_descr list
Here is the function, if you need more code, just ask me.
let rec run()=
let a = create_bloc() in
trace_bloc(a);
let b : char ref = ref 'f' in
while true do
b := 'f';
if !(a.s) = 1 then run() else
let c = get_move() in
if c = Some 'z' then b:= 'z'
else if c = Some 'q' then b:= 'q'
else if c = Some 's' then b:= 's'
else if c = Some 'd' then b:= 'd';
if !b = 'z' || !b = 's' then
if !(a.o) = 4 && !b = 'z' then begin erase_bloc(a) ;a.o := 1; trace_bloc(a); end
else if !b = 'z' && !(a.o) <> 4 then begin erase_bloc(a); a.o := !(a.o)+1; end;
if !b = 's' && !(a.o)=1 then begin erase_bloc(a);a.o := 4;trace_bloc(a);end else
if !b = 's' && !(a.o) <> 1 then begin erase_bloc(a); a.o := !(a.o)-1;trace_bloc(a);end
else if !b = 'd' || !b = 'q' then
decal(a,!b);
dep_bas(a);
Unix.select [] [] [] 0.35;
done;
;;
The Unix.file_descr list * Unix.file_descr list * Unix.file_descr list type is the type of a value that is retuned by the expression Unix.select [] [] [] 0.35;
OCaml doesn't allow you to ignore a value returned by a function that has a type other than unit. You can, however, use the ignore function to tell explicitly to the compiler that you don't need the result of the select function,
ignore (Unix.select [] [] [] 0.35);
You can also use Unix.sleepf function to implement sleeping that supports fractions of seconds, e.g., you can substitute the above expression with just,
Unix.sleepf 0.35
This will as well pause your program for 350 milliseconds.

Yacc NULL in OCaml?

I am implementing the following grammar in OCamlyacc and OCamllex:
The OCaml type declaration for my IF-ELSE clauses is as such:
(* Some code not shown *)
and stmt = ASSIGN of lv * exp
| IF of exp * stmt * stmt
| WHILE of exp * stmt
| DOWHILE of stmt * exp
| READ of id
| PRINT of exp
| BLOCK of block
(* Some code not shown *)
I can define the IF-ELSE portion in OCamlyacc as such:
stmt:
| IF LPAREN e RPAREN stmt { S.IF ($3, $5, ???) } /*line 1*/
| IF LPAREN e RPAREN stmt ELSE stmt { S.IF ($3, $5, $7) } /*line 2*/
But, how can I put "NULL" for a statement type ("stmt"), where the question marks are for the IF-Statement that does not have an ELSE (line 1)? I do not have access to a statement type that only has an expression ("exp") and only one statement.
I thought about putting a "while(0){print(0)}" statement there, but that is not how you are suppose to do it, especially because it will parse in a while-statement when it shouldn't.
One solution can be to explicitly add a nop statement that does by definition nothing.
stmt = ASSIGN of lv * exp
| IFTE of exp * stmt * stmt
| WHILE of exp * stmt
| DOWHILE of stmt * exp
| READ of id
| PRINT of exp
| BLOCK of block
| NOP
If you can't modify your types, you can still do the dummy assigment : lv = lv
Another solution wich may be cleaner imo, is to add explicitly add the
if-then operation to your statements :
stmt = ASSIGN of lv * exp
| IFTE of exp * stmt * stmt
| IFT of exp * stmt
| WHILE of exp * stmt
| DOWHILE of stmt * exp
| READ of id
| PRINT of exp
| BLOCK of block
Thank you ghilesZ for your post, but I am not able to modify the type rules in OCaml. Therefore, adding in a new rule or extending any types is forbidden.
I finally figured out what I had to do. I thought there was something special in OCaml that I was missing, so I purposely left out details of this specific assignment that I thought would just over cloud my question, but in fact were useful.
Let me show my answer to this, but first let me give some extra details:
Therefore, by using the grammar rules up above and the provided OCaml below, the solution would be:
stmt:
| IF LPAREN e RPAREN stmt ELSE stmt { S.IF ($3, $5, $7) } /*line 1*/
| IF LPAREN e RPAREN stmt { S.IF ($3, $5, S.BLOCK ([],[])) } /*line 2*/
You basically keep the ELSE, but you just put in empty curly braces, effectively putting in a "NULL" there or a "do-nothing" action.
Lastly, I flipped the ordering, because for these two IF-statements to be parsed correctly, you need to set precedence, which can be done with:
%nonassoc RPAREN /* Right parenthesis */
%nonassoc ELSE

Targeting a specific occurence in a string and replacing it

I have a string with multiple / in it and I am trying to convert this string in to LaTeX code. Basically (a)/(b) becomes \\dfrac{a}{b}.
The difficulty is that (a) and/or (b) could contain other /.
To respect the parentheses balancing, I would like to replace the / from left to right, and replacing them accordingly to what it around. I made a try but I don't know how target a specific / and replace. using position and length parameters seems to be very complicated.
function ToFrac (s)
while s:find ("/") ~= nil
do
-- Replace : \dfrac{}{}/() -> \dfrac{\dfrac...}{}
if ( s:find ( '\\dfrac%b{}%b{}/%b()' , j ) ~= nil )
then
x,y,num,den = s:find( '(\\dfrac%b{}%b{})/(%b())' )
den = den:gsub( '.(.+).' , '%1' )
s = s:gsub( '(\\dfrac%b{}%b{})/(%b())',
"\\dfrac{"..num.."}{"..den.."}" , 1 )
end
print ('### -- ', s)
-- Replace : ()/\dfrac{}{} -> \dfrac[}]{\dfrac...}
if ( s:find ( '(%b()/\\dfrac%b{}%b{}' ) ~= nil )
then
x,y,num,den = s:find( '((%b())/(\\dfrac%b{}%b{})' )
num = num:gsub( '.(.+).' , '%1' )
s = s:gsub( '((%b())/()\\dfrac%b{}%b{})',
"\\dfrac{"..num.."}{"..den.."}" , 1 )
end
print ('### -- ', s)
-- Replace : ()/() -> \dfrac{}{}
if ( s:find ( '%b()/%b()' , 1 ) ~= nil )
then
x,y,num,den = s:find( '(%b())/(%b())' )
num = num:gsub( '.(.+).' , '%1' )
den = den:gsub( '.(.+).' , '%1' )
s = s:gsub( '(%b())/(%b())',
"\\dfrac{"..num.."}{"..den.."}" , 1 )
Done = true
end
print ('### -- ', s)
end -- while
return (s)
end
s = "((a)/(b))/(c)"
print (s, ToFrac(s))
s = "(a)/((b)/(c))"
print (s, ToFrac(s))
s = "(a)/(b)/(c)/(d))"
print (s, ToFrac(s))
s = "((a)/(b))/((c)/(d))"
print (s, ToFrac(s))
Amended version of rpattiso's idea:
function to_frac(expr)
local t
return expr == '' and '' or (expr..'()'):gsub('(.-)(%b())',
function(prefix, subexpr)
local replace_with = ''
if not prefix:find'^%s*/%s*$' then
t, replace_with = {}, (not t and ''
or t[2] and '\\dfrac{'..t[1]..'}{'..t[2]..'}'
or '('..t[1]..')')..prefix
elseif t[2] then
t = {'\\dfrac{'..t[1]..'}{'..t[2]..'}'}
end
table.insert(t, to_frac(subexpr:sub(2,-2)))
return replace_with
end
)
end
print(to_frac' (a )/((b) / (c))') --> \dfrac{a }{\dfrac{b}{c}}
print(to_frac'((a)/((b)/(c)))/(e)') --> \dfrac{\dfrac{a}{\dfrac{b}{c}}}{e}
print(to_frac'(a)/(b)/(c)/(d)') --> \dfrac{\dfrac{\dfrac{a}{b}}{c}}{d}
The 'replace' argument of string.gsub can be a function.
Using that function, you can apply the substitution recursively to the numerator and denominator and build the result that way. string.sub can be used to remove the parentheses from the numerator and denominator.
function to_frac(expr)
return (expr:gsub('%s*(%b())%s*/%s*(%b())%s*',
function(num, denom)
return '\\dfrac{'..to_frac(num:sub(2,-2))..'}{'
..to_frac(denom:sub(2,-2))..'}'
end))
end
expr = ' (a )/((b) / (c))' -- \dfrac{a }{\dfrac{b}{c}}
print(to_frac(expr))
expr = '((a)/((b)/(c)))/(e)' -->\dfrac{\dfrac{a}{\dfrac{b}{c}}}{e}
print(to_frac(expr))
If you want to go beyond using parentheses for delimiting arguments and obey precedence rules, then look into LPeg.

get last number part of a String in PL/SQL

In PL/SQL, is there any way to calculate next serial number from another one like 'A1B0010C'. Next serial no will be A1B0011C (+1). My idea is retrieve number part, increase it get return string back.
I can do this in java, but passing more than 1000 elements to Oracle will cause problems in IN clause.
So please help, any suggestion is appreciated.
Try to write some recursive function like this
This Function returns next character. ex: D after C. 0 after Z.
create or replace function get_next_character(ch char)
return char
is
result_ch char(1) := ch;
begin
IF UPPER(ch) = 'Z' or ch = '9' THEN
result_ch := 'A';
ELSE
result_ch := chr(ascii(ch) + 1);
END IF;
return upper(result_ch);
end get_next_character;
and this is your actual function which returns the next serial number
So, it generates 'A1B0010D' if your input is 'A1B0010C'
create or replace function get_next_serial(p_serial IN varchar2) -- PASS THE REQUIRED ARGUMENTS
return varchar2
IS
v_ret_serial varchar2(100);
v_in_serial varchar2(100) := p_serial;
tmp varchar2(100);
last_char char(1);
begin
tmp := v_in_serial;
for i in reverse 1..length(v_in_serial) loop
last_char := substr(tmp, length(tmp));
last_char := get_next_character(last_char);
tmp := substr(v_in_serial, 1, length(tmp)-1);
v_in_serial := substr(v_in_serial, 1, i-1) || last_char || substr(v_in_serial, i+1);
IF last_char <> 'A' then
exit;
END IF;
end loop;
IF last_char = 'A' THEN
v_in_serial:= 'A'||v_in_serial;
END IF;
return UPPER(v_in_serial);
exception
when NO_DATA_FOUND then
return 'AA';
when others then
return null;
end get_next_serial;
you can call this function (get_next_serial('abc')) where ever you want;
select get_next_serial('ALK0989KJ') from dual
You can place these two functions in a package and use at your convenience.
You can achieve it by using the following mix of Regexp_* functions:
SQL> with t1 as(
2 select 'A1B0010C' col from dual
3 )
4 select regexp_replace(col, '[[:digit:]]+'
5 , to_char(to_number(regexp_substr(col, '[[:digit:]]+',1,2) + 1), 'fm0000')
6 , 1
7 , 2
8 ) as Res
9 from t1
10 ;
RES
------------
A1B0011C
UPDATE In response to the comment.
SQL> with t1 as(
2 select 'A1B0010C' col from dual union all
3 select 'A1B0010C2' col from dual union all
4 select 'A1B0012C001' col from dual union all
5 select 'S000001' col from dual
6 )
7 select col
8 , regexp_replace(col
9 , '([[:digit:]]+)([[:alpha:]]+$|$)'
10 , LPad(to_char(to_number(num) + 1), length(num), '0') || '\2'
11 ) as Res
12 from (select col
13 , regexp_substr(col, '([[:digit:]]+)([[:alpha:]]+$|$)', 1, 1, 'i', 1) as num
14 from t1
15 )
16 ;
COL RES
----------- -----------------
A1B0010C A1B0011C
A1B0010C2 A1B0010C3
A1B0012C001 A1B0012C002
S000001 S000002
create or replace function nextSerial(s in varchar2) return varchar2 as
i integer;
done boolean := false;
c char;
newserial varchar2(4000);
begin
newserial := s;
i := length(newserial);
while i>0 and not done loop
c := substr(newserial, i, 1);
if c='9' then
newserial := substr(newserial, 1, i-1) || '0' || substr(newserial, i+1);
elsif c>='0' and c<='8' then
c := chr(ascii(c)+1);
newserial := substr(newserial, 1, i-1) || c || substr(newserial, i+1);
done := true;
end if;
i := i-1;
end loop;
if not done then
newserial := '1' || newserial;
end if;
return newserial;
end;