# HG changeset patch # User etisserant # Date 1170253958 -3600 # Node ID fb772792efd13d2831f12df6d8aa2a4f2e2927de Initial commit. Last MatPLC CVS with some makefile inclusion removed in order to compile fine out of MatPLC. diff -r 000000000000 -r fb772792efd1 .cdtproject --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.cdtproject Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 000000000000 -r fb772792efd1 .project --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.project Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,82 @@ + + + matiec + + + + + + org.eclipse.cdt.make.core.makeBuilder + clean,full,incremental, + + + org.eclipse.cdt.make.core.build.arguments + + + + org.eclipse.cdt.core.errorOutputParser + org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser; + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.environment + + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.build.target.inc + all + + + org.eclipse.cdt.make.core.enabledIncrementalBuild + true + + + org.eclipse.cdt.make.core.build.target.clean + clean + + + org.eclipse.cdt.make.core.build.command + make + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + org.eclipse.cdt.make.core.build.target.auto + all + + + org.eclipse.cdt.make.core.stopOnError + false + + + + + org.eclipse.cdt.make.core.ScannerConfigBuilder + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.make.core.makeNature + org.eclipse.cdt.make.core.ScannerConfigNature + org.eclipse.cdt.core.ccnature + + diff -r 000000000000 -r fb772792efd1 .settings/org.eclipse.cdt.core.prefs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.settings/org.eclipse.cdt.core.prefs Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,3 @@ +#Wed Jan 31 15:26:34 CET 2007 +eclipse.preferences.version=1 +indexerId=org.eclipse.cdt.core.fastIndexer diff -r 000000000000 -r fb772792efd1 AnnexF/average_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/average_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,18 @@ + FUNCTION_BLOCK AVERAGE + VAR_INPUT + RUN : BOOL ; (* 1 = run, 0 = reset *) + XIN : REAL ; (* Input variable *) + N : INT ; (* 0 <= N < 128 or manufacturer- *) + END_VAR (* specified maximum value *) + VAR_OUTPUT XOUT : REAL ; END_VAR (* Averaged output *) + VAR SUM : REAL := 0.0; (* Running sum *) + FIFO : DELAY ; (* N-Element FIFO *) + END_VAR + SUM := SUM - FIFO.XOUT ; + FIFO (RUN := RUN , XIN := XIN, N := N) ; + SUM := SUM + FIFO.XOUT ; + IF RUN THEN XOUT := SUM/N ; + ELSE SUM := N*XIN ; XOUT := XIN ; + END_IF ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/cmd_monitor_il.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/cmd_monitor_il.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,37 @@ +FUNCTION_BLOCK CMD_MONITOR + VAR_INPUT AUTO_CMD : BOOL ; (* Automated command *) + AUTO_MODE : BOOL ; (* AUTO_CMD enable *) + MAN_CMD : BOOL ; (* Manual Command *) + MAN_CMD_CHK : BOOL ; (* Negated MAN_CMD to debounce *) + T_CMD_MAX : TIME ; (* Max time from CMD to FDBK *) + FDBK : BOOL ; (* Confirmation of CMD completion + by operative unit *) + ACK : BOOL ; (* Acknowledge/cancel ALRM *) + END_VAR + VAR_OUTPUT CMD : BOOL ; (* Command to operative unit *) + ALRM : BOOL ; (* T_CMD_MAX expired without FDBK *) + END_VAR + VAR CMD_TMR : TON ; (* CMD-to-FDBK timer *) + ALRM_FF : SR ; (* Note over-riding S input: *) + END_VAR (* Command must be cancelled before + "ACK" can cancel alarm *) + (* Function Block Body *) +LD T_CMD_MAX +ST CMD_TMR.PT (* Store an input to the TON FB *) +LD AUTO_CMD +AND AUTO_MODE +OR( MAN_CMD +ANDN AUTO_MODE +ANDN MAN_CMD_CHK +) +ST CMD +IN CMD_TMR (* Invoke the TON FB *) +LD CMD_TMR.Q +ANDN FDBK +ST ALRM_FF.S1 (* Store an input to the SR FB *) +LD ACK +R ALRM_FF (* Invoke the SR FB *) +LD ALRM_FF.Q1 +ST ALRM + +END_FUNCTION_BLOCK diff -r 000000000000 -r fb772792efd1 AnnexF/cmd_monitor_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/cmd_monitor_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,25 @@ +FUNCTION_BLOCK CMD_MONITOR + VAR_INPUT AUTO_CMD : BOOL ; (* Automated command *) + AUTO_MODE : BOOL ; (* AUTO_CMD enable *) + MAN_CMD : BOOL ; (* Manual Command *) + MAN_CMD_CHK : BOOL ; (* Negated MAN_CMD to debounce *) + T_CMD_MAX : TIME ; (* Max time from CMD to FDBK *) + FDBK : BOOL ; (* Confirmation of CMD completion + by operative unit *) + ACK : BOOL ; (* Acknowledge/cancel ALRM *) + END_VAR + VAR_OUTPUT CMD : BOOL ; (* Command to operative unit *) + ALRM : BOOL ; (* T_CMD_MAX expired without FDBK *) + END_VAR + VAR CMD_TMR : TON ; (* CMD-to-FDBK timer *) + ALRM_FF : SR ; (* Note over-riding S input: *) + END_VAR (* Command must be cancelled before + "ACK" can cancel alarm *) + (* Function Block Body *) + CMD := AUTO_CMD & AUTO_MODE + OR MAN_CMD & NOT MAN_CMD_CHK & NOT AUTO_MODE ; + CMD_TMR (IN := CMD, PT := T_CMD_MAX); + ALRM_FF (S1 := CMD_TMR.Q & NOT FDBK, R := ACK); + ALRM := ALRM_FF.Q1; + +END_FUNCTION_BLOCK diff -r 000000000000 -r fb772792efd1 AnnexF/delay_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/delay_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,18 @@ +FUNCTION_BLOCK DELAY (* N-sample delay *) + VAR_INPUT + RUN : BOOL ; (* 1 = run, 0 = reset *) + XIN : REAL ; + N : INT (* 0 <= N < 128 or manufacturer- *) + END_VAR (* specified maximum value *) + VAR_OUTPUT XOUT : REAL; END_VAR (* Delayed output *) + VAR X : ARRAY [0..127] (* N-Element queue *) + OF REAL; (* with FIFO discipline *) + I, IXIN, IXOUT : INT := 0; + END_VAR + IF RUN THEN IXIN := MOD(IXIN + 1, 128) ; X[IXIN] := XIN ; + IXOUT := MOD(IXOUT + 1, 128) ; XOUT := X[IXOUT]; + ELSE XOUT := XIN ; IXIN := N ; IXOUT := 0; + FOR I := 0 TO N DO X[I] := XIN; END_FOR; + END_IF ; +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/derivative_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/derivative_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,18 @@ + FUNCTION_BLOCK DERIVATIVE + VAR_INPUT + RUN : BOOL ; (* 0 = reset *) + XIN : REAL ; (* Input to be differentiated *) + CYCLE : TIME ; (* Sampling period *) + END_VAR + VAR_OUTPUT + XOUT : REAL ; (* Differentiated output *) + END_VAR + VAR X1, X2, X3 : REAL ; END_VAR + IF RUN THEN + XOUT := (3.0 * (XIN - X3) + X1 - X2) + / (10.0 * TIME_TO_REAL(CYCLE)) ; + X3 := X2 ; X2 := X1 ; X1 := XIN ; + ELSE XOUT := 0.0; X1 := XIN ; X2 := XIN ; X3 := XIN ; + END_IF ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/diffeq_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/diffeq_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,30 @@ + FUNCTION_BLOCK DIFFEQ + VAR_INPUT + RUN : BOOL ; (* 1 = run, 0 = reset *) + XIN : REAL ; + A : ARRAY[1..] OF REAL ; (* Input coefficients *) + M : INT ; (* Length of input history *) + B : ARRAY[0..] OF REAL ; (* Output coefficients *) + N : INT ; (* Length of output history *) + END_VAR + VAR_OUTPUT XOUT : REAL := 0.0 ; END_VAR + VAR (* NOTE : Manufacturer may specify other array sizes *) + XI : ARRAY [0..128] OF REAL ; (* Input history *) + XO : ARRAY [0..128] OF REAL ; (* Output history *) + I : INT ; + END_VAR + XO[0] := XOUT ; XI[0] := XIN ; + XOUT := B[0] * XIN ; + IF RUN THEN + FOR I := M TO 1 BY -1 DO + XOUT := XOUT + A[I] * XO[I] ; XO[I] := XO[I-1]; + END_FOR; + FOR I := N TO 1 BY -1 DO + XOUT := XOUT + B[I] * XI[I] ; XI[I] := XI[I-1]; + END_FOR; + ELSE + FOR I := 1 TO M DO XO[I] := 0.0; END_FOR; + FOR I := 1 TO N DO XI[I] := 0.0; END_FOR; + END_IF ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/fwd_rev_mon_il.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/fwd_rev_mon_il.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,77 @@ +FUNCTION_BLOCK FWD_REV_MON +VAR_INPUT AUTO : BOOL ;(* Enable automated commands *) + ACK : BOOL ; (* Acknowledge/cancel all alarms *) + AUTO_FWD : BOOL ; (* Automated forward command *) + MAN_FWD : BOOL ; (* Manual forward command *) + MAN_FWD_CHK : BOOL ; (* Negated MAN_FWD for debouncing *) + T_FWD_MAX : TIME ; (* Maximum time from FWD_CMD to FWD_FDBK *) + FWD_FDBK : BOOL ; (* Confirmation of FWD_CMD completion *) + (* by operative unit *) + AUTO_REV : BOOL ; (* Automated reverse command *) + MAN_REV : BOOL ; (* Manual reverse command *) + MAN_REV_CHK : BOOL ; (* Negated MAN_REV for debouncing *) + T_REV_MAX : TIME ; (* Maximum time from REV_CMD to REV_FDBK *) + REV_FDBK : BOOL ; (* Confirmation of REV_CMD completion *) +END_VAR (* by operative unit *) +VAR_OUTPUT KLAXON : BOOL ; (* Any alarm active *) + FWD_REV_ALRM : BOOL; (* Forward/reverse command conflict *) + FWD_CMD : BOOL ; (* "Forward" command to operative unit *) + FWD_ALRM : BOOL ; (* T_FWD_MAX expired without FWD_FDBK *) + REV_CMD : BOOL ; (* "Reverse" command to operative unit *) + REV_ALRM : BOOL ; (* T_REV_MAX expired without REV_FDBK *) +END_VAR +VAR FWD_MON : CMD_MONITOR; (* "Forward" command monitor *) + REV_MON : CMD_MONITOR; (* "Reverse" command monitor *) + FWD_REV_FF : SR ; (* Forward/Reverse contention latch *) +END_VAR +(* Function Block body *) +LD AUTO (* Load common inputs *) +ST FWD_MON.AUTO_MODE +ST REV_MON.AUTO_MODE +LD ACK +ST FWD_MON.ACK +ST REV_MON.ACK +ST FWD_REV_FF.R +LD AUTO_FWD (* Load inputs to FWD_MON *) +ST FWD_MON.AUTO_CMD +LD MAN_FWD +ST FWD_MON.MAN_CMD +LD MAN_FWD_CHK +ST FWD_MON.MAN_CMD_CHK +LD T_FWD_MAX +ST FWD_MON.T_CMD_MAX +LD FWD_FDBK +ST FWD_MON.FDBK +CAL FWD_MON (* Activate FWD_MON *) +LD AUTO_REV (* Load inputs to REV_MON *) +ST REV_MON.AUTO_CMD +LD MAN_REV +ST REV_MON.MAN_CMD +LD MAN_REV_CHK +ST REV_MON.MAN_CMD_CHK +LD T_REV_MAX +ST REV_MON.T_CMD_MAX +LD REV_FDBK +ST REV_MON.FDBK +CAL REV_MON (* Activate REV_MON *) +LD FWD_MON.CMD (* Check for contention *) +AND REV_MON.CMD +S1 FWD_REV_FF (* Latch contention condition *) +LD FWD_REV_FF.Q +ST FWD_REV_ALRM (* Contention alarm *) +LD FWD_MON.CMD (* "Forward" command and alarm *) +ANDN FWD_REV_ALRM +ST FWD_CMD +LD FWD_MON.ALRM +ST FWD_ALRM +LD REV_MON.CMD (* "Reverse" command and alarm *) +ANDN FWD_REV_ALRM +ST REV_CMD +LD REV_MON.ALRM +ST REV_ALRM +OR FWD_ALRM (* OR all alarms *) +OR FWD_REV_ALRM +ST KLAXON + +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/fwd_rev_mon_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/fwd_rev_mon_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,53 @@ +FUNCTION_BLOCK FWD_REV_MON +VAR_INPUT AUTO : BOOL ;(* Enable automated commands *) + ACK : BOOL ; (* Acknowledge/cancel all alarms *) + AUTO_FWD : BOOL ; (* Automated forward command *) + MAN_FWD : BOOL ; (* Manual forward command *) + MAN_FWD_CHK : BOOL ; (* Negated MAN_FWD for debouncing *) + T_FWD_MAX : TIME ; (* Maximum time from FWD_CMD to FWD_FDBK *) + FWD_FDBK : BOOL ; (* Confirmation of FWD_CMD completion *) + (* by operative unit *) + AUTO_REV : BOOL ; (* Automated reverse command *) + MAN_REV : BOOL ; (* Manual reverse command *) + MAN_REV_CHK : BOOL ; (* Negated MAN_REV for debouncing *) + T_REV_MAX : TIME ; (* Maximum time from REV_CMD to REV_FDBK *) + REV_FDBK : BOOL ; (* Confirmation of REV_CMD completion *) +END_VAR (* by operative unit *) +VAR_OUTPUT KLAXON : BOOL ; (* Any alarm active *) + FWD_REV_ALRM : BOOL; (* Forward/reverse command conflict *) + FWD_CMD : BOOL ; (* "Forward" command to operative unit *) + FWD_ALRM : BOOL ; (* T_FWD_MAX expired without FWD_FDBK *) + REV_CMD : BOOL ; (* "Reverse" command to operative unit *) + REV_ALRM : BOOL ; (* T_REV_MAX expired without REV_FDBK *) +END_VAR +VAR FWD_MON : CMD_MONITOR; (* "Forward" command monitor *) + REV_MON : CMD_MONITOR; (* "Reverse" command monitor *) + FWD_REV_FF : SR ; (* Forward/Reverse contention latch *) +END_VAR +(* Function Block body *) +(* Evaluate internal function blocks *) + FWD_MON (AUTO_MODE := AUTO, + ACK := ACK, + AUTO_CMD := AUTO_FWD, + MAN_CMD := MAN_FWD, + MAN_CMD_CHK := MAN_FWD_CHK, + T_CMD_MAX := T_FWD_MAX, + FDBK := FWD_FDBK); + REV_MON (AUTO_MODE := AUTO, + ACK := ACK, + AUTO_CMD := AUTO_REV, + MAN_CMD := MAN_REV, + MAN_CMD_CHK := MAN_REV_CHK, + T_CMD_MAX := T_REV_MAX, + FDBK := REV_FDBK); + FWD_REV_FF (S1 := FWD_MON.CMD & REV_MON.CMD, R := ACK); +(* Transfer data to outputs *) + FWD_REV_ALRM := FWD_REV_FF.Q1; + FWD_CMD := FWD_MON.CMD & NOT FWD_REV_ALRM; + FWD_ALRM := FWD_MON.ALRM; + REV_CMD := REV_MON.CMD & NOT FWD_REV_ALRM; + REV_ALRM := REV_MON.ALRM; + KLAXON := FWD_ALRM OR REV_ALRM OR FWD_REV_ALRM; + +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/gravel_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/gravel_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,129 @@ +PROGRAM GRAVEL (* Gravel measurement and loading system *) + VAR_INPUT + OFF_PB : BOOL ; + ON_PB : BOOL ; + FILL_PB : BOOL ; + SIREN_ACK : BOOL ; + LOAD_PB : BOOL ; (* Load truck from bin *) + JOG_PB : BOOL ; + LAMP_TEST : BOOL ; + TRUCK_ON_RAMP : BOOL ; (* Optical sensor *) + SILO_EMPTY_LS : BOOL ; + BIN_EMPTY_LS : BOOL ; + SETPOINT : BYTE ; (* 2-digit BCD *) + END_VAR + VAR_OUTPUT + CONTROL_LAMP : BOOL ; + TRUCK_LAMP : BOOL ; + SILO_EMPTY_LAMP : BOOL ; + CONVEYOR_LAMP : BOOL ; + CONVEYOR_MOTOR : BOOL ; + SILO_VALVE : BOOL ; + BIN_VALVE : BOOL ; + SIREN : BOOL ; + BIN_LEVEL : BYTE ; + END_VAR + VAR + BLINK_TIME : TIME; (* BLINK ON/OFF time *) + PULSE_TIME : TIME; (* LEVEL_CTR increment interval *) + RUNOUT_TIME: TIME; (* Conveyor running time after loading *) + RUN_IN_TIME: TIME; (* Conveyor running time before loading *) + SILENT_TIME: TIME; (* Siren silent time after SIREN_ACK *) + OK_TO_RUN : BOOL; (* 1 = Conveyor is allowed to run *) + (* Function Blocks *) + BLINK: TON; (* Blinker OFF period timer / ON output *) + BLANK: TON; (* Blinker ON period timer / blanking pulse *) + PULSE: TON; (* LEVEL_CTR pulse interval timer *) + SIREN_FF: RS; + SILENCE_TMR: TP; (* Siren silent period timer *) + END_VAR + VAR RETAIN LEVEL_CTR : CTU ; END_VAR + (* Program body *) + (* Major operating states *) + INITIAL_STEP START : END_STEP + TRANSITION FROM START TO FILL_BIN + := FILL_PB & CONTROL.X ; END_TRANSITION + STEP FILL_BIN: SILO_VALVE(N); END_STEP + TRANSITION FROM FILL_BIN TO START + := NOT FILL_PB OR NOT CONTROL.X ; END_TRANSITION + TRANSITION FROM FILL_BIN TO LOAD_WAIT := LEVEL_CTR.Q ; + END_TRANSITION + STEP LOAD_WAIT : END_STEP + TRANSITION FROM LOAD_WAIT TO RUN_IN + := LOAD_PB & OK_TO_RUN ; END_TRANSITION + STEP RUN_IN : END_STEP + TRANSITION FROM RUN_IN TO LOAD_WAIT := NOT OK_TO_RUN ; + END_TRANSITION + TRANSITION FROM RUN_IN TO DUMP_BIN + := RUN_IN.T > RUN_IN_TIME; + END_TRANSITION + STEP DUMP_BIN: BIN_VALVE(N); END_STEP + TRANSITION FROM DUMP_BIN TO LOAD_WAIT := NOT OK_TO_RUN ; + END_TRANSITION + TRANSITION FROM DUMP_BIN TO RUNOUT := BIN_EMPTY_LS ; + END_TRANSITION + STEP RUNOUT : END_STEP + TRANSITION FROM RUNOUT TO LOAD_WAIT := NOT OK_TO_RUN ; + END_TRANSITION + TRANSITION FROM RUNOUT TO START + := RUNOUT.T >= RUNOUT_TIME ; END_TRANSITION +(* Control state sequencing *) +INITIAL_STEP CONTROL_OFF: END_STEP +TRANSITION FROM CONTROL_OFF TO CONTROL + := ON_PB & NOT OFF_PB ; END_TRANSITION +STEP CONTROL: CONTROL_ACTION(N); END_STEP +ACTION CONTROL_ACTION: + BLINK(EN:=CONTROL.X & NOT BLANK.Q, PT := BLINK_TIME) ; + BLANK(EN:=BLINK.Q, PT := BLINK_TIME) ; + OK_TO_RUN := CONTROL.X & TRUCK_ON_RAMP ; + CONVEYOR_MOTOR := + OK_TO_RUN & OR(JOG_PB, RUN_IN.X, DUMP_BIN.X, RUNOUT.X); +END_ACTION +TRANSITION FROM CONTROL TO CONTROL_OFF := OFF_PB ; +END_TRANSITION +(* Monitor Logic *) +INITIAL_STEP MONITOR: MONITOR_ACTION(N); END_STEP +ACTION MONITOR_ACTION: + CONVEYOR_LAMP := CONVEYOR_MOTOR & BLINK.Q ; + CONTROL_LAMP := CONTROL.X OR LAMP_TEST ; + TRUCK_LAMP := TRUCK_ON_RAMP OR LAMP_TEST ; + SILO_EMPTY_LAMP := BLINK.Q & SILO_EMPTY_LS OR LAMP_TEST ; + SILENCE_TMR(IN:=SIREN_ACK, PT:=SILENT_TIME) ; + SIREN_FF(S:=SILO_EMPTY_LS, R1:=SILENCE_TMR.Q) ; + SIREN := SIREN_FF.Q1 ; + PULSE(IN:=FILL_BIN.X & NOT PULSE.Q, PT:=PULSE_TIME) ; + LEVEL_CTR(R := BIN_EMPTY_LS, CU := PULSE.Q, + PV := BCD_TO_INT(SETPOINT)) ; + BIN_LEVEL := INT_TO_BCD(LEVEL_CTR.CV) ; +END_ACTION + +END_PROGRAM + + CONFIGURATION GRAVEL_CONTROL + RESOURCE PROC1 ON PROC_TYPE_Y + PROGRAM G : GRAVEL + (* Inputs *) + (OFF_PB := %I0.0 , + ON_PB := %I0.1 , + FILL_PB := %I0.2 , + SIREN_ACK := %I0.3 , + LOAD_PB := %I0.4 , + JOG_PB := %I0.5 , + LAMP_TEST := %I0.7 , + TRUCK_ON_RAMP := %I1.4 , + SILO_EMPTY_LS := %I1.5 , + BIN_EMPTY_LS := %I1.6 , + SETPOINT := %IB2 , + (* Outputs *) + CONTROL_LAMP => %Q4.0, + TRUCK_LAMP => %Q4.2, + SILO_EMPTY_LAMP => %Q4.3, + CONVEYOR_LAMP => %Q5.3, + CONVEYOR_MOTOR => %Q5.4, + SILO_VALVE => %Q5.5, + BIN_VALVE => %Q5.6, + SIREN => %Q5.7, + BIN_LEVEL => %B6) ; + END_RESOURCE + END_CONFIGURATION + diff -r 000000000000 -r fb772792efd1 AnnexF/hysteresis_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/hysteresis_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,10 @@ +FUNCTION_BLOCK HYSTERESIS + (* Boolean hysteresis on difference *) + (* of REAL inputs, XIN1 - XIN2 *) + VAR_INPUT XIN1, XIN2, EPS : REAL; END_VAR + VAR_OUTPUT Q : BOOL := 0; END_VAR + IF Q THEN IF XIN1 < (XIN2 - EPS) THEN Q := 0; END_IF ; + ELSIF XIN1 > (XIN2 + EPS) THEN Q := 1 ; + END_IF ; +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/integral_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/integral_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,18 @@ + FUNCTION_BLOCK INTEGRAL + VAR_INPUT + RUN : BOOL ; (* 1 = integrate, 0 = hold *) + R1 : BOOL ; (* Overriding reset *) + XIN : REAL ; (* Input variable *) + X0 : REAL ; (* Initial value *) + CYCLE : TIME ; (* Sampling period *) + END_VAR + VAR_OUTPUT + Q : BOOL ; (* NOT R1 *) + XOUT : REAL ; (* Integrated output *) + END_VAR + Q := NOT R1 ; + IF R1 THEN XOUT := X0 ; + ELSIF RUN THEN XOUT := XOUT + XIN * TIME_TO_REAL(CYCLE); + END_IF ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/lag1_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/lag1_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,16 @@ + FUNCTION_BLOCK LAG1 + VAR_INPUT + RUN : BOOL ; (* 1 = run, 0 = reset *) + XIN : REAL ; (* Input variable *) + TAU : TIME ; (* Filter time constant *) + CYCLE : TIME ; (* Sampling time interval *) + END_VAR + VAR_OUTPUT XOUT : REAL ; END_VAR (* Filtered output *) + VAR K : REAL ; (* Smoothing constant, 0.0<=K<1.0 *) + END_VAR + IF RUN THEN XOUT := XOUT + K * (XIN - XOUT) ; + ELSE XOUT := XIN ; + K := TIME_TO_REAL(CYCLE) / TIME_TO_REAL(CYCLE + TAU) ; + END_IF ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/pid_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/pid_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,25 @@ + FUNCTION_BLOCK PID + VAR_INPUT + AUTO : BOOL ; (* 0 - manual , 1 - automatic *) + PV : REAL ; (* Process variable *) + SP : REAL ; (* Set point *) + X0 : REAL ; (* Manual output adjustment - *) + (* Typically from transfer station *) + KP : REAL ; (* Proportionality constant *) + TR : REAL ; (* Reset time *) + TD : REAL ; (* Derivative time constant *) + CYCLE : TIME ; (* Sampling period *) + END_VAR + VAR_OUTPUT XOUT : REAL; END_VAR + VAR ERROR : REAL ; (* PV - SP *) + ITERM : INTEGRAL ; (* FB for integral term *) + DTERM : DERIVATIVE ; (* FB for derivative term *) + END_VAR + ERROR := PV - SP ; + (*** Adjust ITERM so that XOUT := X0 when AUTO = 0 ***) + ITERM (RUN := AUTO, R1 := NOT AUTO, XIN := ERROR, + X0 := TR * (X0 - ERROR), CYCLE := CYCLE) ; + DTERM (RUN := AUTO, XIN := ERROR, CYCLE := CYCLE) ; + XOUT := KP * (ERROR + ITERM.XOUT/TR + DTERM.XOUT*TD) ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/ramp_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/ramp_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,25 @@ + FUNCTION_BLOCK RAMP + VAR_INPUT + RUN : BOOL ; (* 0 - track X0, 1 - ramp to/track X1 *) + X0,X1 : REAL ; + TR : TIME ; (* Ramp duration *) + CYCLE : TIME ; (* Sampling period *) + END_VAR + VAR_OUTPUT + BUSY : BOOL ; (* BUSY = 1 during ramping period *) + XOUT : REAL := 0.0 ; + END_VAR + VAR XI : REAL ; (* Initial value *) + T : TIME := T#0s; (* Elapsed time of ramp *) + END_VAR + BUSY := RUN ; + IF RUN THEN + IF T >= TR THEN BUSY := 0 ; XOUT := X1 ; + ELSE XOUT := XI + (X1-XI) * TIME_TO_REAL(T) + / TIME_TO_REAL(TR) ; + T := T + CYCLE ; + END_IF ; + ELSE XOUT := X0 ; XI := X0 ; T := t#0s ; + END_IF ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/readme --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/readme Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,6 @@ + IEC 61131-3 Annex F Examples (informative) + +This directory contains example code take from: + +Annex F of Draft 2nd edition of the IEC 61131-3 standard (1998) +"PROGRAMMABLE CONTROLLERS - PROGRAMMING LANGUAGES" diff -r 000000000000 -r fb772792efd1 AnnexF/stack_int_il.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/stack_int_il.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,58 @@ +FUNCTION_BLOCK STACK_INT + VAR_INPUT PUSH, POP: BOOL R_EDGE; (* Basic stack operations *) + R1 : BOOL ; (* Over-riding reset *) + IN : INT ; (* Input to be pushed *) + N : INT ; (* Maximum depth after reset *) + END_VAR + VAR_OUTPUT EMPTY : BOOL := 1 ; (* Stack empty *) + OFLO : BOOL := 0 ; (* Stack overflow *) + OUT : INT := 0 ; (* Top of stack data *) + END_VAR + VAR STK : ARRAY[0..127] OF INT; (* Internal stack *) + NI : INT :=128 ; (* Storage for N upon reset *) + PTR : INT := -1 ; (* Stack pointer *) + END_VAR + (* Function Block body *) + LD R1 (* Dispatch on operations *) + JMPC RESET + LD POP + ANDN EMPTY (* Don't pop empty stack *) + JMPC POP_STK + LD PUSH + ANDN OFLO (* Don't push overflowed stack *) + JMPC PUSH_STK + RET (* Return if no operations active *) +RESET: LD 0 (* Stack reset operations *) + ST OFLO + LD 1 + ST EMPTY + LD -1 + ST PTR + CAL LIMIT(MN:=1,IN:=N,MX:=128) + ST NI + JMP ZRO_OUT +POP_STK: LD 0 + ST OFLO (* Popped stack is not overflowing *) + LD PTR + SUB 1 + ST PTR + LT 0 (* Empty when PTR < 0 *) + ST EMPTY + JMPC ZRO_OUT + LD STK[PTR] + JMP SET_OUT +PUSH_STK: LD 0 + ST EMPTY (* Pushed stack is not empty *) + LD PTR + ADD 1 + ST PTR + EQ NI (* Overflow when PTR = NI *) + ST OFLO + JMPC ZRO_OUT + LD IN + ST STK[PTR] (* Push IN onto STK *) + JMP SET_OUT +ZRO_OUT: LD 0 (* OUT=0 for EMPTY or OFLO *) +SET_OUT: ST OUT + + END_FUNCTION_BLOCK diff -r 000000000000 -r fb772792efd1 AnnexF/stack_int_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/stack_int_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,31 @@ +FUNCTION_BLOCK STACK_INT + VAR_INPUT PUSH, POP: BOOL R_EDGE; (* Basic stack operations *) + R1 : BOOL ; (* Over-riding reset *) + IN : INT ; (* Input to be pushed *) + N : INT ; (* Maximum depth after reset *) + END_VAR + VAR_OUTPUT EMPTY : BOOL := 1 ; (* Stack empty *) + OFLO : BOOL := 0 ; (* Stack overflow *) + OUT : INT := 0 ; (* Top of stack data *) + END_VAR + VAR STK : ARRAY[0..127] OF INT; (* Internal stack *) + NI : INT :=128 ; (* Storage for N upon reset *) + PTR : INT := -1 ; (* Stack pointer *) + END_VAR + (* Function Block body *) + IF R1 THEN + OFLO := 0; EMPTY := 1; PTR := -1; + NI := LIMIT (MN:=1,IN:=N,MX:=128); OUT := 0; + ELSIF POP & NOT EMPTY THEN + OFLO := 0; PTR := PTR-1; EMPTY := PTR < 0; + IF EMPTY THEN OUT := 0; + ELSE OUT := STK[PTR]; + END_IF ; + ELSIF PUSH & NOT OFLO THEN + EMPTY := 0; PTR := PTR+1; OFLO := (PTR = NI); + IF NOT OFLO THEN OUT := IN ; STK[PTR] := IN; + ELSE OUT := 0; + END_IF ; + END_IF ; + + END_FUNCTION_BLOCK diff -r 000000000000 -r fb772792efd1 AnnexF/transfer_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/transfer_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,25 @@ +FUNCTION_BLOCK TRANSFER + VAR_INPUT + AUTO : BOOL ; (* 1 - track X0, 0 - ramp or hold *) + XIN : REAL ; (* Typically from PID Function Block *) + FAST_RATE, SLOW_RATE : REAL ; (* Up/down ramp slopes *) + FAST_UP, SLOW_UP, (* Typically pushbuttons *) + FAST_DOWN, SLOW_DOWN : BOOL; + CYCLE : TIME ; (* Sampling period *) + END_VAR + VAR_OUTPUT XOUT : REAL ; END_VAR + VAR XFER_RAMP : INTEGRAL ; + RAMP_RATE : REAL ; + END_VAR + RAMP_RATE := 0.0 ; + IF NOT AUTO THEN + IF FAST_UP THEN RAMP_RATE := FAST_RATE; END_IF; + IF SLOW_UP THEN RAMP_RATE := RAMP_RATE + SLOW_RATE; END_IF; + IF FAST_DOWN THEN RAMP_RATE := RAMP_RATE - FAST_RATE; END_IF; + IF SLOW_DOWN THEN RAMP_RATE := RAMP_RATE - SLOW_RATE; END_IF; + END_IF ; + XFER_RAMP (RUN := 1, CYCLE := CYCLE, R1 := AUTO, + XIN := RAMP_RATE, X0 := XIN) ; + XOUT := XFER_RAMP.XOUT; +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 AnnexF/weigh_il.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/weigh_il.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,18 @@ +FUNCTION WEIGH : WORD (* BCD encoded *) + VAR_INPUT (* "EN" input is used to indicate "scale ready" *) + weigh_command : BOOL; + gross_weight : WORD ; (* BCD encoded *) + tare_weight : INT ; + END_VAR +(* Function Body *) + LD weigh_command + JMPC WEIGH_NOW + ST ENO (* No weighing, 0 to "ENO" *) + RET +WEIGH_NOW: LD gross_weight + BCD_TO_INT + SUB tare_weight + INT_TO_BCD (* Return evaluated weight *) + ST WEIGH + +END_FUNCTION (* Implicit "ENO" *) diff -r 000000000000 -r fb772792efd1 AnnexF/weigh_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AnnexF/weigh_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,12 @@ +FUNCTION WEIGH : WORD (* BCD encoded *) + VAR_INPUT (* "EN" input is used to indicate "scale ready" *) + weigh_command : BOOL; + gross_weight : WORD ; (* BCD encoded *) + tare_weight : INT ; + END_VAR +(* Function Body *) +IF weigh_command THEN + WEIGH := INT_TO_BCD (BCD_TO_INT(gross_weight) - tare_weight); +END_IF ; + +END_FUNCTION (* Implicit "ENO" *) diff -r 000000000000 -r fb772792efd1 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,63 @@ +# include the system specific Makefile +#include ../../Makefile.$(shell uname) + +default: all + +all: iec2cc iec2iec + +install: all + install -d $(INSTALL_PREDIR)/$(INSTALL_BINDIR)/ + install -d $(INSTALL_PREDIR)/$(IECLIBDIR)/ + install iec2cc $(INSTALL_PREDIR)/$(INSTALL_BINDIR)/ + install lib/*.txt $(INSTALL_PREDIR)/$(IECLIBDIR)/ +# install iec2iec $(INSTALL_PREDIR)/$(INSTALL_BINDIR)/ + + +uninstall: + rm -f $(INSTALL_PREDIR)/$(INSTALL_BINDIR)/iec2cc + rm -f $(INSTALL_PREDIR)/$(INSTALL_BINDIR)/iec2iec + for ff in `cd lib; ls *.txt; cd ..` do echo $$ff done +# for ff in `cd lib; ls *.txt; cd ..` do rm -f $(INSTALL_PREDIR)/$(IECLIBDIR)/$$ff done + + +clean: + -rm -f iec2iec iec2cc *.o absyntax/*.o Makefile.depend +# make something everywhere (ie, in all Makefiles that have that target) + find . -depth -mindepth 2 -maxdepth 2 -name Makefile -printf %h\\n | xargs -i make -C{} $@ + + + +#get warnings, debugging information and optimization +CXXFLAGS = -Wall -pedantic -Wpointer-arith -Wwrite-strings +# CXXFLAGS += -Werror +CXXFLAGS += -ggdb -O3 -funroll-loops +# Note: if the optimizer crashes, we'll leave out the -O3 for those files + +CXXFLAGS += -I. + + + +LIBS = absyntax/absyntax.o absyntax/visitor.o +LIBS += stage1_2/iec.y.o stage1_2/iec.flex.o + +iec2cc: main.o stage4/generate_cc/generate_cc.o stage4/stage4.o $(LIBS) + $(CXX) -o iec2cc main.o stage4/stage4.o stage4/generate_cc/generate_cc.o $(LIBS) + + +iec2iec: main.o stage4/generate_iec/generate_iec.o stage4/stage4.o $(LIBS) + $(CXX) -o iec2iec main.o stage4/stage4.o stage4/generate_iec/generate_iec.o $(LIBS) + + +#how to make things in subdirectories etc +../% /% absyntax/% stage1_2/% stage3/% stage4/% util/%: + $(MAKE) -C $(@D) $(@F) + +Makefile.depend depend: + $(CXX) -MM -MG -I. *.cc \ + | perl -pe 's/:/ Makefile.depend:/' > Makefile.depend + +include Makefile.depend + + + + diff -r 000000000000 -r fb772792efd1 absyntax/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax/Makefile Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,30 @@ +# include the system specific Makefile +#include ../../../Makefile.$(shell uname) + + + + +default: all + +all: absyntax.o visitor.o + +clean: + rm -f *.o + find . -depth -mindepth 2 -maxdepth 2 -name Makefile -printf %h\\n | xargs -i make -C{} $@ + + +#get warnings, debugging information and optimization +CXXFLAGS = -Wall -pedantic -Wpointer-arith -Wwrite-strings +# CXXFLAGS += -Werror + +CXXFLAGS += -ggdb -O3 -funroll-loops +# Note: if the optimizer crashes, we'll leave out the -O3 for those files + +CXXFLAGS += -I. -I../* -I../../absyntax + + + +#how to make things in subdirectories etc +../% /% absyntax/% stage1_2/% stage3/% stage4/% util/%: + $(MAKE) -C $(@D) $(@F) + diff -r 000000000000 -r fb772792efd1 absyntax/absyntax.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax/absyntax.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,159 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + * Definition of the Abstract Syntax data structure components + */ + +#include +#include /* required for exit() */ +#include + +#include "absyntax.hh" +//#include "../stage1_2/iec.hh" /* required for BOGUS_TOKEN_ID, etc... */ +#include "visitor.hh" + +#define ABORT(str) {printf("ERROR: %s\n", str); exit(0);} + + + +symbol_c *tree_root = NULL; + + + + +/* The base class of all symbols */ +symbol_c::symbol_c(void) { + lineno = 0; +} + +symbol_c::symbol_c(long lineno) { + this->lineno = lineno; +} + + + +token_c::token_c(const char *value) { + this->value = value; +// printf("New token: %s\n", value); +} + + + + + + +list_c::list_c(void) { + n = 0; + elements = NULL; +} + +list_c::list_c(symbol_c *elem) { + n = 0; + elements = NULL; + add_element(elem); +} + +/* insert a new element */ +void list_c::add_element(symbol_c *elem) { +//printf("list_c::add_element()\n"); + n++; + elements = (symbol_c **)realloc(elements, n * sizeof(symbol_c *)); + if (elements == NULL) + ABORT("Out of memory"); + elements[n - 1] = elem; +} + + + + + +#define SYM_LIST(class_name_c) \ +void *class_name_c::accept(visitor_c &visitor) {return visitor.visit(this);} + +#define SYM_TOKEN(class_name_c) \ +class_name_c::class_name_c(const char *value): token_c(value) {} \ +void *class_name_c::accept(visitor_c &visitor) {return visitor.visit(this);} + +#define SYM_REF0(class_name_c) \ +void *class_name_c::accept(visitor_c &visitor) {return visitor.visit(this);} + + +#define SYM_REF2(class_name_c, ref1, ref2) \ +class_name_c::class_name_c(symbol_c *ref1, \ + symbol_c *ref2) { \ + this->ref1 = ref1; \ + this->ref2 = ref2; \ +} \ +void *class_name_c::accept(visitor_c &visitor) {return visitor.visit(this);} + + +#define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4) \ +class_name_c::class_name_c(symbol_c *ref1, \ + symbol_c *ref2, \ + symbol_c *ref3, \ + symbol_c *ref4) { \ + this->ref1 = ref1; \ + this->ref2 = ref2; \ + this->ref3 = ref3; \ + this->ref4 = ref4; \ +} \ +void *class_name_c::accept(visitor_c &visitor) {return visitor.visit(this);} + + +#define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6) \ +class_name_c::class_name_c(symbol_c *ref1, \ + symbol_c *ref2, \ + symbol_c *ref3, \ + symbol_c *ref4, \ + symbol_c *ref5, \ + symbol_c *ref6) { \ + this->ref1 = ref1; \ + this->ref2 = ref2; \ + this->ref3 = ref3; \ + this->ref4 = ref4; \ + this->ref5 = ref5; \ + this->ref6 = ref6; \ +} \ +void *class_name_c::accept(visitor_c &visitor) {return visitor.visit(this);} + + + +#include "absyntax.def" + + + + +#undef SYM_LIST +#undef SYM_TOKEN +#undef SYM_TOKEN +#undef SYM_REF0 +#undef SYM_REF2 +#undef SYM_REF4 +#undef SYM_REF6 + + + + + diff -r 000000000000 -r fb772792efd1 absyntax/absyntax.def --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax/absyntax.def Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,852 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + * Definition of the Abstract Syntax data structure components + */ + + +/* + * ABSYNTAX.DEF + * + * This generates the parse tree structure used to bind the components + * identified by Bison in the correct syntax order. At the end of the + * Bison analysis the tree is walked in a sequential fashion generating + * the relavent code. + */ + + +/***************************/ +/* 2.1.6 - Pragmas */ +/***************************/ +SYM_TOKEN(pragma_c) + + +/***************************/ +/* B 0 - Programming Model */ +/***************************/ +SYM_LIST(library_c) + + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +SYM_TOKEN(identifier_c) + + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ +SYM_TOKEN(real_c) +SYM_TOKEN(integer_c) +SYM_TOKEN(binary_integer_c) +SYM_TOKEN(octal_integer_c) +SYM_TOKEN(hex_integer_c) + +/* Note: + * We do not have signed_integer_c and signed_real_c classes. + * These are stored in the parse tree as a integer_c or real_c + * preceded by a unary minus operator. + */ +/* Not required: +SYM_TOKEN(signed_integer_c) +SYM_TOKEN(signed_real_c) +*/ + +SYM_REF2(numeric_literal_c, type, value) +SYM_REF2(integer_literal_c, type, value) +SYM_REF2(real_literal_c, type, value) +SYM_REF2(bit_string_literal_c, type, value) +SYM_REF2(boolean_literal_c, type, value) + +/* helper class for boolean_literal_c */ +SYM_REF0(boolean_true_c) + +/* helper class for boolean_literal_c */ +SYM_REF0(boolean_false_c) + + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ +SYM_TOKEN(double_byte_character_string_c) +SYM_TOKEN(single_byte_character_string_c) + + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ + +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +SYM_REF0(neg_time_c) +SYM_REF2(duration_c, neg, interval) +SYM_TOKEN(fixed_point_c) +SYM_REF2(days_c, days, hours) +SYM_REF2(hours_c, hours, minutes) +SYM_REF2(minutes_c, minutes, seconds) +SYM_REF2(seconds_c, seconds, milliseconds) +SYM_REF2(milliseconds_c, milliseconds, unused) + + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ +SYM_REF2(time_of_day_c, daytime, unused) +SYM_REF4(daytime_c, day_hour, day_minute, day_second, unused) +SYM_REF2(date_c, date_literal, unused) +SYM_REF4(date_literal_c, year, month, day, unused) +SYM_REF2(date_and_time_c, date_literal, daytime) + + +/**********************/ +/* B.1.3 - Data types */ +/**********************/ +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ +SYM_REF0(time_type_name_c) +SYM_REF0(bool_type_name_c) +SYM_REF0(sint_type_name_c) +SYM_REF0(int_type_name_c) +SYM_REF0(dint_type_name_c) +SYM_REF0(lint_type_name_c) +SYM_REF0(usint_type_name_c) +SYM_REF0(uint_type_name_c) +SYM_REF0(udint_type_name_c) +SYM_REF0(ulint_type_name_c) +SYM_REF0(real_type_name_c) +SYM_REF0(lreal_type_name_c) +SYM_REF0(date_type_name_c) +SYM_REF0(tod_type_name_c) +SYM_REF0(dt_type_name_c) +SYM_REF0(byte_type_name_c) +SYM_REF0(word_type_name_c) +SYM_REF0(dword_type_name_c) +SYM_REF0(lword_type_name_c) +SYM_REF0(string_type_name_c) +SYM_REF0(wstring_type_name_c) + + + +/********************************/ +/* B.1.3.2 - Generic data types */ +/********************************/ + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +/* TYPE type_declaration_list END_TYPE */ +SYM_REF2(data_type_declaration_c, type_declaration_list, unused) + +/* helper symbol for data_type_declaration */ +SYM_LIST(type_declaration_list_c) + +/* simple_type_name ':' simple_spec_init */ +SYM_REF2(simple_type_declaration_c, simple_type_name, simple_spec_init) + + +/* simple_specification ASSIGN constant */ +SYM_REF2(simple_spec_init_c, simple_specification, constant) + +/* subrange_type_name ':' subrange_spec_init */ +SYM_REF2(subrange_type_declaration_c, subrange_type_name, subrange_spec_init) + +/* subrange_specification ASSIGN signed_integer */ +SYM_REF2(subrange_spec_init_c, subrange_specification, signed_integer) + +/* integer_type_name '(' subrange')' */ +SYM_REF2(subrange_specification_c, integer_type_name, subrange) + +/* signed_integer DOTDOT signed_integer */ +SYM_REF2(subrange_c, lower_limit, upper_limit) + +/* enumerated_type_name ':' enumerated_spec_init */ +SYM_REF2(enumerated_type_declaration_c, enumerated_type_name, enumerated_spec_init) + +/* enumerated_specification ASSIGN enumerated_value */ +SYM_REF2(enumerated_spec_init_c, enumerated_specification, enumerated_value) + +/* helper symbol for enumerated_specification->enumerated_spec_init */ +/* enumerated_value_list ',' enumerated_value */ +SYM_LIST(enumerated_value_list_c) + +/* enumerated_type_name '#' identifier */ +SYM_REF2(enumerated_value_c, type, value) + +/* identifier ':' array_spec_init */ +SYM_REF2(array_type_declaration_c, identifier, array_spec_init) + +/* array_specification [ASSIGN array_initialization} */ +/* array_initialization may be NULL ! */ +SYM_REF2(array_spec_init_c, array_specification, array_initialization) + +/* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ +SYM_REF2(array_specification_c, array_subrange_list, non_generic_type_name) + +/* helper symbol for array_specification */ +/* array_subrange_list ',' subrange */ +SYM_LIST(array_subrange_list_c) + +/* array_initialization: '[' array_initial_elements_list ']' */ +/* helper symbol for array_initialization */ +/* array_initial_elements_list ',' array_initial_elements */ +SYM_LIST(array_initial_elements_list_c) + +/* integer '(' [array_initial_element] ')' */ +/* array_initial_element may be NULL ! */ +SYM_REF2(array_initial_elements_c, integer, array_initial_element) + +/* structure_type_name ':' structure_specification */ +SYM_REF2(structure_type_declaration_c, structure_type_name, structure_specification) + +/* structure_type_name ASSIGN structure_initialization */ +/* structure_initialization may be NULL ! */ +SYM_REF2(initialized_structure_c, structure_type_name, structure_initialization) + +/* helper symbol for structure_declaration */ +/* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ +/* structure_element_declaration_list structure_element_declaration ';' */ +SYM_LIST(structure_element_declaration_list_c) + +/* structure_element_name ':' *_spec_init */ +SYM_REF2(structure_element_declaration_c, structure_element_name, spec_init) + +/* helper symbol for structure_initialization */ +/* structure_initialization: '(' structure_element_initialization_list ')' */ +/* structure_element_initialization_list ',' structure_element_initialization */ +SYM_LIST(structure_element_initialization_list_c) + +/* structure_element_name ASSIGN value */ +SYM_REF2(structure_element_initialization_c, structure_element_name, value) + +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +/* + * NOTE: + * (Summary: Contrary to what is expected, the + * string_type_declaration_c is not used to store + * simple string type declarations that do not include + * size limits. + * For e.g.: + * str1_type: STRING := "hello!" + * will be stored in a simple_type_declaration_c + * instead of a string_type_declaration_c. + * The following: + * str2_type: STRING [64] := "hello!" + * will be stored in a sring_type_declaration_c + * + * Read on for why this is done... + * End Summary) + * + * According to the spec, the valid construct + * TYPE new_str_type : STRING := "hello!"; END_TYPE + * has two possible routes to type_declaration... + * + * Route 1: + * type_declaration: single_element_type_declaration + * single_element_type_declaration: simple_type_declaration + * simple_type_declaration: identifier ':' simple_spec_init + * simple_spec_init: simple_specification ASSIGN constant + * (shift: identifier <- 'new_str_type') + * simple_specification: elementary_type_name + * elementary_type_name: STRING + * (shift: elementary_type_name <- STRING) + * (reduce: simple_specification <- elementary_type_name) + * (shift: constant <- "hello!") + * (reduce: simple_spec_init: simple_specification ASSIGN constant) + * (reduce: ...) + * + * + * Route 2: + * type_declaration: string_type_declaration + * string_type_declaration: identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init + * (shift: identifier <- 'new_str_type') + * elementary_string_type_name: STRING + * (shift: elementary_string_type_name <- STRING) + * (shift: string_type_declaration_size <- empty ) + * string_type_declaration_init: ASSIGN character_string + * (shift: character_string <- "hello!") + * (reduce: string_type_declaration_init <- ASSIGN character_string) + * (reduce: string_type_declaration <- identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init ) + * (reduce: type_declaration <- string_type_declaration) + * + * + * At first glance it seems that removing route 1 would make + * the most sense. Unfortunately the construct 'simple_spec_init' + * shows up multiple times in other rules, so changing this construct + * would also mean changing all the rules in which it appears. + * I (Mario) therefore chose to remove route 2 instead. This means + * that the above declaration gets stored in a + * simple_type_declaration_c, and not in a string_type_declaration_c + * as would be expected! + */ +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +SYM_REF4(string_type_declaration_c, string_type_name, + elementary_string_type_name, + string_type_declaration_size, + string_type_declaration_init) /* may be == NULL! */ + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +SYM_REF2(symbolic_variable_c, var_name, unused) + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +SYM_TOKEN(direct_variable_c) + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +SYM_REF2(array_variable_c, subscripted_variable, subscript_list) + +/* subscript_list ',' subscript */ +SYM_LIST(subscript_list_c) + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a tructured variable! + * Code handling a structured_variable_c must take + * this into account! + */ +SYM_REF2(structured_variable_c, record_variable, field_selector) + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ +SYM_REF0(constant_option_c) +SYM_REF0(retain_option_c) +SYM_REF0(non_retain_option_c) + +/* option -> the RETAIN/NON_RETAIN/ directive... */ +SYM_REF2(input_declarations_c, option, input_declaration_list) + +/* helper symbol for input_declarations */ +SYM_LIST(input_declaration_list_c) + +/* edge -> The F_EDGE or R_EDGE directive */ +SYM_REF2(edge_declaration_c, edge, var1_list) + +SYM_REF0(raising_edge_option_c) +SYM_REF0(falling_edge_option_c) + +/* spec_init is one of the following... + * simple_spec_init_c * + * subrange_spec_init_c * + * enumerated_spec_init_c * + */ +SYM_REF2(var1_init_decl_c, var1_list, spec_init) + +/* | var1_list ',' variable_name */ +SYM_LIST(var1_list_c) + +/* var1_list ':' array_spec_init */ +SYM_REF2(array_var_init_decl_c, var1_list, array_spec_init) + +/* var1_list ':' initialized_structure */ +SYM_REF2(structured_var_init_decl_c, var1_list, initialized_structure) + +/* fb_name_list ':' function_block_type_name ASSIGN structure_initialization */ +/* structure_initialization -> may be NULL ! */ +SYM_REF4(fb_name_decl_c, fb_name_list, function_block_type_name, structure_initialization, unused) + +/* fb_name_list ',' fb_name */ +SYM_LIST(fb_name_list_c) + +/* VAR_OUTPUT [RETAIN | NON_RETAIN] var_init_decl_list END_VAR */ +/* option -> may be NULL ! */ +SYM_REF2(output_declarations_c, option, var_init_decl_list) + +/* VAR_IN_OUT var_declaration_list END_VAR */ +SYM_REF2(input_output_declarations_c, var_declaration_list, unused) + +/* helper symbol for input_output_declarations */ +/* var_declaration_list var_declaration ';' */ +SYM_LIST(var_declaration_list_c) + +/* var1_list ':' array_specification */ +SYM_REF2(array_var_declaration_c, var1_list, array_specification) + +/* var1_list ':' structure_type_name */ +SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) + +/* VAR [CONSTANT] var_init_decl_list END_VAR */ +/* option -> may be NULL ! */ +SYM_REF2(var_declarations_c, option, var_init_decl_list) + +/* VAR RETAIN var_init_decl_list END_VAR */ +SYM_REF2(retentive_var_declarations_c, var_init_decl_list, unused) + +/* VAR [CONSTANT|RETAIN|NON_RETAIN] located_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +SYM_REF2(located_var_declarations_c, option, located_var_decl_list) + +/* helper symbol for located_var_declarations */ +/* located_var_decl_list located_var_decl ';' */ +SYM_LIST(located_var_decl_list_c) + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +SYM_REF4(located_var_decl_c, variable_name, location, located_var_spec_init, unused) + +/*| VAR_EXTERNAL [CONSTANT] external_declaration_list END_VAR */ +/* option -> may be NULL ! */ +SYM_REF2(external_var_declarations_c, option, external_declaration_list) + +/* helper symbol for external_var_declarations */ +/*| external_declaration_list external_declaration';' */ +SYM_LIST(external_declaration_list_c) + +/* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ +SYM_REF2(external_declaration_c, global_var_name, specification) + +/*| VAR_GLOBAL [CONSTANT|RETAIN] global_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +SYM_REF2(global_var_declarations_c, option, global_var_decl_list) + +/* helper symbol for global_var_declarations */ +/*| global_var_decl_list global_var_decl ';' */ +SYM_LIST(global_var_decl_list_c) + +/*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */ +/* type_specification ->may be NULL ! */ +SYM_REF2(global_var_decl_c, global_var_spec, type_specification) + +/*| global_var_name location */ +SYM_REF2(global_var_spec_c, global_var_name, location) + +/* AT direct_variable */ +SYM_REF2(location_c, direct_variable, unused) + +/*| global_var_list ',' global_var_name */ +SYM_LIST(global_var_list_c) + +/* var1_list ':' single_byte_string_spec */ +SYM_REF2(single_byte_string_var_declaration_c, var1_list, single_byte_string_spec) + +/* STRING ['[' integer ']'] [ASSIGN single_byte_character_string] */ +/* integer ->may be NULL ! */ +/* single_byte_character_string ->may be NULL ! */ +SYM_REF2(single_byte_string_spec_c, integer, single_byte_character_string) + +/* var1_list ':' double_byte_string_spec */ +SYM_REF2(double_byte_string_var_declaration_c, var1_list, double_byte_string_spec) + +/* WSTRING ['[' integer ']'] [ASSIGN double_byte_character_string] */ +/* integer ->may be NULL ! */ +/* double_byte_character_string ->may be NULL ! */ +SYM_REF2(double_byte_string_spec_c, integer, double_byte_character_string) + +/*| VAR [RETAIN|NON_RETAIN] incompl_located_var_decl_list END_VAR */ +/* option ->may be NULL ! */ +SYM_REF2(incompl_located_var_declarations_c, option, incompl_located_var_decl_list) + +/* helper symbol for incompl_located_var_declarations */ +/*| incompl_located_var_decl_list incompl_located_var_decl ';' */ +SYM_LIST(incompl_located_var_decl_list_c) + +/* variable_name incompl_location ':' var_spec */ +SYM_REF4(incompl_located_var_decl_c, variable_name, incompl_location, var_spec, unused) + +/* AT incompl_location_token */ +SYM_TOKEN(incompl_location_c) + +/* intermediate helper symbol for: + * - non_retentive_var_decls + * - output_declarations + */ +SYM_LIST(var_init_decl_list_c) + + + + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +SYM_REF4(function_declaration_c, derived_function_name, type_name, var_declarations_list, function_body) + +/* intermediate helper symbol for + * - function_declaration + * - function_block_declaration + * - program_declaration + */ +SYM_LIST(var_declarations_list_c) + +/* option -> storage method, CONSTANT or */ +SYM_REF2(function_var_decls_c, option, decl_list) + +/* intermediate helper symbol for function_var_decls */ +SYM_LIST(var2_init_decl_list_c) + + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ +/* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ +SYM_REF4(function_block_declaration_c, fblock_name, var_declarations, fblock_body, unused) + +/* intermediate helper symbol for function_declaration */ +/* { io_var_declarations | other_var_declarations } */ +/* + * NOTE: we re-use the var_declarations_list_c + */ + +/* VAR_TEMP temp_var_decl_list END_VAR */ +SYM_REF2(temp_var_decls_c, var_decl_list, unused) + +/* intermediate helper symbol for temp_var_decls */ +SYM_LIST(temp_var_decls_list_c) + +/* VAR NON_RETAIN var_init_decl_list END_VAR */ +SYM_REF2(non_retentive_var_decls_c, var_decl_list, unused) + + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +/* PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */ +SYM_REF4(program_declaration_c, program_type_name, var_declarations, function_block_body, unused) + +/* intermediate helper symbol for program_declaration_c */ +/* { io_var_declarations | other_var_declarations } */ +/* + * NOTE: we re-use the var_declarations_list_c + */ + + + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ + +/* +CONFIGURATION configuration_name + optional_global_var_declarations + (resource_declaration_list | single_resource_declaration) + optional_access_declarations + optional_instance_specific_initializations +END_CONFIGURATION +*/ +SYM_REF6(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, unused) + +/* helper symbol for configuration_declaration */ +SYM_LIST(resource_declaration_list_c) + +/* +RESOURCE resource_name ON resource_type_name + optional_global_var_declarations + single_resource_declaration +END_RESOURCE +*/ +SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration) + +/* task_configuration_list program_configuration_list */ +SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) + +/* helper symbol for single_resource_declaration */ +SYM_LIST(task_configuration_list_c) + +/* helper symbol for single_resource_declaration */ +SYM_LIST(program_configuration_list_c) + +/* helper symbol for + * - access_path + * - instance_specific_init + */ +SYM_LIST(any_fb_name_list_c) + +/* [resource_name '.'] global_var_name ['.' structure_element_name] */ +SYM_REF4(global_var_reference_c, resource_name, global_var_name, structure_element_name, unused) + +/* prev_declared_program_name '.' symbolic_variable */ +SYM_REF2(program_output_reference_c, program_name, symbolic_variable) + +/* TASK task_name task_initialization */ +SYM_REF2(task_configuration_c, task_name, task_initialization) + +/* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ +SYM_REF4(task_initialization_c, single_data_source, interval_data_source, priority_data_source, unused) + +/* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ +SYM_REF6(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, unused) + +/* prog_conf_elements ',' prog_conf_element */ +SYM_LIST(prog_conf_elements_c) + +/* fb_name WITH task_name */ +SYM_REF2(fb_task_c, fb_name, task_name) + +/* any_symbolic_variable ASSIGN prog_data_source */ +SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) + +/* any_symbolic_variable SENDTO data_sink */ +SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, prog_data_source) + +/* VAR_CONFIG instance_specific_init_list END_VAR */ +SYM_REF2(instance_specific_initializations_c, instance_specific_init_list, unused) + +/* helper symbol for instance_specific_initializations */ +SYM_LIST(instance_specific_init_list_c) + +/* resource_name '.' program_name '.' {fb_name '.'} + ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization)) +*/ +SYM_REF6(instance_specific_init_c, resource_name, program_name, any_fb_name_list, variable_name, location, initialization) + +/* helper symbol for instance_specific_init */ +/* function_block_type_name ':=' structure_initialization */ +SYM_REF2(fb_initialization_c, function_block_type_name, structure_initialization) + + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +/*| instruction_list il_instruction */ +SYM_LIST(instruction_list_c) + +/* | label ':' [il_incomplete_instruction] eol_list */ +SYM_REF2(il_instruction_c, label, il_instruction) + + +/* | il_simple_operator [il_operand] */ +SYM_REF2(il_simple_operation_c, il_simple_operator, il_operand) + +/* | function_name [il_operand_list] */ +SYM_REF2(il_function_call_c, function_name, il_operand_list) + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +SYM_REF4(il_expression_c, il_expr_operator, il_operand, simple_instr_list, unused) + +/* il_jump_operator label */ +SYM_REF2(il_jump_operation_c, il_jump_operator, label) + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list) + + +/* | function_name '(' eol_list [il_param_list] ')' */ +SYM_REF2(il_formal_funct_call_c, function_name, il_param_list) + +/* | il_operand_list ',' il_operand */ +SYM_LIST(il_operand_list_c) + +/* | simple_instr_list il_simple_instruction */ +SYM_LIST(simple_instr_list_c) + +/* | il_initial_param_list il_param_instruction */ +SYM_LIST(il_param_list_c) + +/* il_assign_operator il_operand + * | il_assign_operator '(' eol_list simple_instr_list ')' + */ +SYM_REF4(il_param_assignment_c, il_assign_operator, il_operand, simple_instr_list, unused) + +/* il_assign_out_operator variable */ +SYM_REF2(il_param_out_assignment_c, il_assign_out_operator, variable) + + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +SYM_REF0(LD_operator_c) +SYM_REF0(LDN_operator_c) +SYM_REF0(ST_operator_c) +SYM_REF0(STN_operator_c) +SYM_REF0(NOT_operator_c) +SYM_REF0(S_operator_c) +SYM_REF0(R_operator_c) +SYM_REF0(S1_operator_c) +SYM_REF0(R1_operator_c) +SYM_REF0(CLK_operator_c) +SYM_REF0(CU_operator_c) +SYM_REF0(CD_operator_c) +SYM_REF0(PV_operator_c) +SYM_REF0(IN_operator_c) +SYM_REF0(PT_operator_c) +SYM_REF0(AND_operator_c) +SYM_REF0(OR_operator_c) +SYM_REF0(XOR_operator_c) +SYM_REF0(ANDN_operator_c) +SYM_REF0(ORN_operator_c) +SYM_REF0(XORN_operator_c) +SYM_REF0(ADD_operator_c) +SYM_REF0(SUB_operator_c) +SYM_REF0(MUL_operator_c) +SYM_REF0(DIV_operator_c) +SYM_REF0(MOD_operator_c) +SYM_REF0(GT_operator_c) +SYM_REF0(GE_operator_c) +SYM_REF0(EQ_operator_c) +SYM_REF0(LT_operator_c) +SYM_REF0(LE_operator_c) +SYM_REF0(NE_operator_c) +SYM_REF0(CAL_operator_c) +SYM_REF0(CALC_operator_c) +SYM_REF0(CALCN_operator_c) +SYM_REF0(RET_operator_c) +SYM_REF0(RETC_operator_c) +SYM_REF0(RETCN_operator_c) +SYM_REF0(JMP_operator_c) +SYM_REF0(JMPC_operator_c) +SYM_REF0(JMPCN_operator_c) + +/*| [NOT] any_identifier SENDTO */ +SYM_REF2(il_assign_out_operator_c, option, variable_name) + + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +SYM_REF2(or_expression_c, l_exp, r_exp) +SYM_REF2(xor_expression_c, l_exp, r_exp) +SYM_REF2(and_expression_c, l_exp, r_exp) +SYM_REF2(equ_expression_c, l_exp, r_exp) +SYM_REF2(notequ_expression_c, l_exp, r_exp) +SYM_REF2(lt_expression_c, l_exp, r_exp) +SYM_REF2(gt_expression_c, l_exp, r_exp) +SYM_REF2(le_expression_c, l_exp, r_exp) +SYM_REF2(ge_expression_c, l_exp, r_exp) +SYM_REF2(add_expression_c, l_exp, r_exp) +SYM_REF2(sub_expression_c, l_exp, r_exp) +SYM_REF2(mul_expression_c, l_exp, r_exp) +SYM_REF2(div_expression_c, l_exp, r_exp) +SYM_REF2(mod_expression_c, l_exp, r_exp) +SYM_REF2(power_expression_c, l_exp, r_exp) +SYM_REF2(neg_expression_c, exp, unused) +SYM_REF2(not_expression_c, exp, unused) + +SYM_REF2(function_invocation_c, function_name, parameter_assignment_list) + + +/********************/ +/* B 3.2 Statements */ +/********************/ +SYM_LIST(statement_list_c) + + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +SYM_REF2(assignment_statement_c, l_exp, r_exp) + + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +/* RETURN */ +SYM_REF0(return_statement_c) + +/* fb_name '(' [param_assignment_list] ')' */ +/* param_assignment_list -> may be NULL ! */ +SYM_REF2(fb_invocation_c, fb_name, param_assignment_list) + +/* helper symbol for fb_invocation */ +/* param_assignment_list ',' param_assignment */ +SYM_LIST(param_assignment_list_c) + +/* variable_name ASSIGN expression */ +SYM_REF2(input_variable_param_assignment_c, variable_name, expression) + +/* [NOT] variable_name '=>' variable */ +SYM_REF4(output_variable_param_assignment_c, not_param, variable_name, variable, unused) + +/* helper CLASS for output_variable_param_assignment */ +SYM_REF0(not_paramassign_c) + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ +/* IF expression THEN statement_list elseif_statement_list ELSE statement_list END_IF */ +SYM_REF4(if_statement_c, expression, statement_list, elseif_statement_list, else_statement_list) + +/* helper symbol for if_statement */ +SYM_LIST(elseif_statement_list_c) + +/* helper symbol for elseif_statement_list */ +/* ELSIF expression THEN statement_list */ +SYM_REF2(elseif_statement_c, expression, statement_list) + +/* CASE expression OF case_element_list ELSE statement_list END_CASE */ +SYM_REF4(case_statement_c, expression, case_element_list, statement_list, unused) + +/* helper symbol for case_statement */ +SYM_LIST(case_element_list_c) + +/* case_list ':' statement_list */ +SYM_REF2(case_element_c, case_list, statement_list) + +SYM_LIST(case_list_c) + + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ +/* FOR control_variable ASSIGN expression TO expression [BY expression] DO statement_list END_FOR */ +SYM_REF6(for_statement_c, control_variable, beg_expression, end_expression, by_expression, statement_list, unused) + +/* WHILE expression DO statement_list END_WHILE */ +SYM_REF2(while_statement_c, expression, statement_list) + +/* REPEAT statement_list UNTIL expression END_REPEAT */ +SYM_REF2(repeat_statement_c, statement_list, expression) + +/* EXIT */ +SYM_REF0(exit_statement_c) + + diff -r 000000000000 -r fb772792efd1 absyntax/absyntax.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax/absyntax.hh Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,189 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + * Declaration of the Abstract Syntax data structure components + */ + +/* + * ABSYNTAX.H + * + * This generates the parse tree structure used to bind the components + * identified by Bison in the correct syntax order. At the end of the + * Bison analysis the tree is walked in a sequential fashion generating + * the relavent code. + */ + +#ifndef _ABSYNTAX_HH +#define _ABSYNTAX_HH + + +#include // required for NULL + +/* Forward declaration of the visitor interface + * dclared in the visitor.hh file + * We cannot include the visitor.hh file, as it will + * include this same file first, as it too requires references + * to the abstract syntax classes defined here. + */ +class visitor_c; // forward declaration + + +class symbol_c; // forward declaration +//extern symbol_c *tree_root; + + + +/* The base class of all symbols */ +class symbol_c { + + public: + /* + * Line number for the purposes of error checking + */ + long lineno; + + public: + /* default constructor */ + symbol_c(void); + symbol_c(long lineno); + + /* default destructor */ + /* must be virtual so compiler does not complain... */ + virtual ~symbol_c(void) {return;}; + + virtual void *accept(visitor_c &visitor) {return NULL;}; +}; + + +class token_c: public symbol_c { + public: + /* the value of the symbol. */ + const char *value; + + public: + token_c(const char *value); +}; + + + /* a list of symbols... */ +class list_c: public symbol_c { + public: + int n; + symbol_c **elements; + + public: + list_c(void); + list_c(symbol_c *elem); + /* insert a new element */ + virtual void add_element(symbol_c *elem); +}; + + + + +#define SYM_LIST(class_name_c) \ +class class_name_c: public list_c { \ + public: \ + virtual void *accept(visitor_c &visitor); \ +}; + + +#define SYM_TOKEN(class_name_c) \ +class class_name_c: public token_c { \ + public: \ + class_name_c(const char *value); \ + virtual void *accept(visitor_c &visitor); \ +}; + + +#define SYM_REF0(class_name_c) \ +class class_name_c: public symbol_c { \ + public: \ + virtual void *accept(visitor_c &visitor); \ +}; + + +#define SYM_REF2(class_name_c, ref1, ref2) \ +class class_name_c: public symbol_c { \ + public: \ + symbol_c *ref1; \ + symbol_c *ref2; \ + public: \ + class_name_c(symbol_c *ref1, \ + symbol_c *ref2 = NULL); \ + virtual void *accept(visitor_c &visitor); \ +}; + + +#define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4) \ +class class_name_c: public symbol_c { \ + public: \ + symbol_c *ref1; \ + symbol_c *ref2; \ + symbol_c *ref3; \ + symbol_c *ref4; \ + public: \ + class_name_c(symbol_c *ref1, \ + symbol_c *ref2, \ + symbol_c *ref3, \ + symbol_c *ref4 = NULL); \ + virtual void *accept(visitor_c &visitor); \ +}; + + +#define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6) \ +class class_name_c: public symbol_c { \ + public: \ + symbol_c *ref1; \ + symbol_c *ref2; \ + symbol_c *ref3; \ + symbol_c *ref4; \ + symbol_c *ref5; \ + symbol_c *ref6; \ + public: \ + class_name_c(symbol_c *ref1, \ + symbol_c *ref2, \ + symbol_c *ref3, \ + symbol_c *ref4, \ + symbol_c *ref5, \ + symbol_c *ref6 = NULL); \ + virtual void *accept(visitor_c &visitor); \ +}; + + +#include "absyntax.def" + + + +#undef SYM_LIST +#undef SYM_TOKEN +#undef SYM_REF0 +#undef SYM_REF2 +#undef SYM_REF4 +#undef SYM_REF6 + + + +#endif /* _ABSYNTAX_HH */ diff -r 000000000000 -r fb772792efd1 absyntax/visitor.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax/visitor.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,265 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * VISITOR.CC + * + * Three base implementations of the visitor interface, + * that may be later extended to execute a particular algorithm. + * + * The null (class null_visitor_c) does nothing. + * + * The iterator (class iterator_visitor_c) iterates through + * every object in the syntax tree. + * + * The search class (class search_visitor_c) iterates through + * every object, until one returns a value != NULL. + */ + + + + + +#include + +#include /* required for NULL */ +#include "visitor.hh" + +#include + + +/******************/ +/* visitor_c */ +/******************/ + +visitor_c::~visitor_c(void) {return;} + + +/******************/ +/* null_visitor_c */ +/******************/ + +null_visitor_c::~null_visitor_c(void) {return;} + +#define SYM_LIST(class_name_c) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_TOKEN(class_name_c) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF0(class_name_c) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF2(class_name_c, ref1, ref2) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6) \ + void *null_visitor_c::visit(class_name_c *symbol) {return NULL;} + + +#include "absyntax.def" + + + + +#undef SYM_LIST +#undef SYM_TOKEN +#undef SYM_REF0 +#undef SYM_REF2 +#undef SYM_REF4 +#undef SYM_REF6 + + + + + + +/**********************/ +/* iterator_visitor_c */ +/**********************/ + +iterator_visitor_c::~iterator_visitor_c(void) {return;} + + +void *iterator_visitor_c::visit_list(list_c *list) { + for(int i = 0; i < list->n; i++) { + list->elements[i]->accept(*this); + } + return NULL; +} + + +#define SYM_LIST(class_name_c) \ + void *iterator_visitor_c::visit(class_name_c *symbol) {return visit_list(symbol);} + +#define SYM_TOKEN(class_name_c) \ + void *iterator_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF0(class_name_c) \ + void *iterator_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF2(class_name_c, ref1, ref2) \ +void *iterator_visitor_c::visit(class_name_c *symbol) { \ + if (symbol->ref1!=NULL) symbol->ref1->accept(*this); \ + if (symbol->ref2!=NULL) symbol->ref2->accept(*this); \ + return NULL; \ +} + +#define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4) \ +void *iterator_visitor_c::visit(class_name_c *symbol) { \ + if (symbol->ref1) symbol->ref1->accept(*this); \ + if (symbol->ref2) symbol->ref2->accept(*this); \ + if (symbol->ref3) symbol->ref3->accept(*this); \ + if (symbol->ref4) symbol->ref4->accept(*this); \ + return NULL; \ +} + + +#define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6) \ +void *iterator_visitor_c::visit(class_name_c *symbol) { \ + if (symbol->ref1) symbol->ref1->accept(*this); \ + if (symbol->ref2) symbol->ref2->accept(*this); \ + if (symbol->ref3) symbol->ref3->accept(*this); \ + if (symbol->ref4) symbol->ref4->accept(*this); \ + if (symbol->ref5) symbol->ref5->accept(*this); \ + if (symbol->ref6) symbol->ref6->accept(*this); \ + return NULL; \ +} + + + +#include "absyntax.def" + + + +#undef SYM_LIST +#undef SYM_TOKEN +#undef SYM_REF0 +#undef SYM_REF2 +#undef SYM_REF4 +#undef SYM_REF6 + + + + + + + + + + +/********************/ +/* search_visitor_c */ +/********************/ + +search_visitor_c::~search_visitor_c(void) {return;} + + +void *search_visitor_c::visit_list(list_c *list) { + for(int i = 0; i < list->n; i++) { + void *res = list->elements[i]->accept(*this); + if (res != NULL) + return res; + } + return NULL; +} + + +#define SYM_LIST(class_name_c) \ + void *search_visitor_c::visit(class_name_c *symbol) {return visit_list(symbol);} + +#define SYM_TOKEN(class_name_c) \ + void *search_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF0(class_name_c) \ + void *search_visitor_c::visit(class_name_c *symbol) {return NULL;} + +#define SYM_REF2(class_name_c, ref1, ref2) \ +void *search_visitor_c::visit(class_name_c *symbol) { \ + void *res = NULL; \ + if (symbol->ref1) res = symbol->ref1->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref2) return symbol->ref2->accept(*this); \ + return NULL; \ +} + +#define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4) \ +void *search_visitor_c::visit(class_name_c *symbol) { \ + void *res = NULL; \ + if (symbol->ref1) res = symbol->ref1->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref2) res = symbol->ref2->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref3) res = symbol->ref3->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref4) return symbol->ref4->accept(*this); \ + return NULL; \ +} + + +#define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6) \ +void *search_visitor_c::visit(class_name_c *symbol) { \ + void *res = NULL; \ + if (symbol->ref1) res = symbol->ref1->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref2) res = symbol->ref2->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref3) res = symbol->ref3->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref4) res = symbol->ref4->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref5) res = symbol->ref5->accept(*this); \ + if (res != NULL) return res; \ + if (symbol->ref6) return symbol->ref6->accept(*this); \ + return NULL; \ +} + + + +#include "absyntax.def" + + + +#undef SYM_LIST +#undef SYM_TOKEN +#undef SYM_REF0 +#undef SYM_REF2 +#undef SYM_REF4 +#undef SYM_REF6 + + + + + + + + + + + + diff -r 000000000000 -r fb772792efd1 absyntax/visitor.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/absyntax/visitor.hh Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,171 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * VISITOR.HH + * + * + * The asbract syntax tree is scaned several times + * during the whole compilation process. For e.g., stage 3 verifies + * semantics, and stage 4 produces output. + * + * Since the number of functions that need to scan through + * the abtract syntax tree may increase in time, (e.g. new + * output formats, new semantic checks, etc...) while the + * abstract syntax tree class structure is pretty stable + * (i.e. the ST and IL languages will not be changing much...), + * we use the 'visitor' design patern. + * + * It is not strictly necessary to use this pattern, but it does + * have the advantage of bringing together the functions + * that implement the same algorithm, each on a different + * class of object in the abstract syntax tree. + * + * + * This file contains the interface that each visitor class + * must implement in order to be able to visit the abstract + * syntax tree (class visitor_c) + * + * Three implementations of this interface are also provided, + * that may be later extended to execute a particular algorithm. + * + * The null (class null_visitor_c) does nothing. + * + * The iterator (class iterator_visitor_c) iterates through + * every object in the syntax tree. + * + * The search class (class search_visitor_c) iterates through + * every object, until one returns a value != NULL. + */ + + +#ifndef _VISITOR_HH +#define _VISITOR_HH + + + + +#include "absyntax.hh" + + + +#define SYM_LIST(class_name_c) \ + virtual void *visit(class_name_c *symbol) = 0; + +#define SYM_TOKEN(class_name_c) \ + virtual void *visit(class_name_c *symbol) = 0; + +#define SYM_REF0(class_name_c) \ + virtual void *visit(class_name_c *symbol) = 0; + +#define SYM_REF2(class_name_c, ref1, ref2) \ + virtual void *visit(class_name_c *symbol) = 0; + +#define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4) \ + virtual void *visit(class_name_c *symbol) = 0; + +#define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6) \ + virtual void *visit(class_name_c *symbol) = 0; + +class visitor_c { + public: + #include "absyntax.def" + + virtual ~visitor_c(void); +}; + +#undef SYM_LIST +#undef SYM_TOKEN +#undef SYM_REF0 +#undef SYM_REF2 +#undef SYM_REF4 +#undef SYM_REF6 + + + + + + +#define SYM_LIST(class_name_c) \ + virtual void *visit(class_name_c *symbol); + +#define SYM_TOKEN(class_name_c) \ + virtual void *visit(class_name_c *symbol); + +#define SYM_REF0(class_name_c) \ + virtual void *visit(class_name_c *symbol); + +#define SYM_REF2(class_name_c, ref1, ref2) \ + virtual void *visit(class_name_c *symbol); + +#define SYM_REF4(class_name_c, ref1, ref2, ref3, ref4) \ + virtual void *visit(class_name_c *symbol); + +#define SYM_REF6(class_name_c, ref1, ref2, ref3, ref4, ref5, ref6) \ + virtual void *visit(class_name_c *symbol); + + + + + +class null_visitor_c: public visitor_c { + public: + #include "absyntax.def" + + virtual ~null_visitor_c(void); +}; + + + +class iterator_visitor_c: public visitor_c { + protected: + void *visit_list(list_c *list); + + public: + #include "absyntax.def" + + virtual ~iterator_visitor_c(void); +}; + + +class search_visitor_c: public visitor_c { + protected: + void *visit_list(list_c *list); + + public: + #include "absyntax.def" + + virtual ~search_visitor_c(void); +}; + + +#undef SYM_LIST +#undef SYM_TOKEN +#undef SYM_REF0 +#undef SYM_REF2 +#undef SYM_REF4 +#undef SYM_REF6 + +#endif /* _VISITOR_HH */ diff -r 000000000000 -r fb772792efd1 lib/bcd_to_int.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/bcd_to_int.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,98 @@ +(* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + +(* + * This is part of the library conatining the functions + * and function blocks defined in the standard. + * + * Conversion from BCD + * ------------------- + *) + + + +FUNCTION BYTE_BCD_TO_USINT: USINT + VAR_INPUT + BCD : BYTE; + END_VAR + + BYTE_BCD_TO_USINT := BCD MOD 16; + BCD := BCD / 16; + BYTE_BCD_TO_USINT := BYTE_BCD_TO_USINT + 10*BCD; +END_FUNCTION + + + + +FUNCTION WORD_BCD_TO_UINT: UINT + VAR_INPUT + BCD : WORD; + END_VAR + + WORD_BCD_TO_UINT := BCD MOD 16; + BCD := BCD / 16; + WORD_BCD_TO_UINT := WORD_BCD_TO_UINT + 10*(BCD MOD 16); + BCD := BCD / 16; + WORD_BCD_TO_UINT := WORD_BCD_TO_UINT + 100*(BCD MOD 16); + BCD := BCD / 16; + WORD_BCD_TO_UINT := WORD_BCD_TO_UINT + 1000*BCD; +END_FUNCTION + + + + +FUNCTION DWORD_BCD_TO_UDINT: UDINT + VAR_INPUT + BCD : DWORD; + END_VAR + + DWORD_BCD_TO_UDINT := BCD MOD 16; + BCD := BCD / 16; + DWORD_BCD_TO_UDINT := DWORD_BCD_TO_UDINT + 10*(BCD MOD 16); + BCD := BCD / 16; + DWORD_BCD_TO_UDINT := DWORD_BCD_TO_UDINT + 100*(BCD MOD 16); + BCD := BCD / 16; + DWORD_BCD_TO_UDINT := DWORD_BCD_TO_UDINT + 1000*(BCD MOD 16); + BCD := BCD / 16; + DWORD_BCD_TO_UDINT := DWORD_BCD_TO_UDINT + 1_0000*(BCD MOD 16); + BCD := BCD / 16; + DWORD_BCD_TO_UDINT := DWORD_BCD_TO_UDINT + 10_0000*(BCD MOD 16); + BCD := BCD / 16; + DWORD_BCD_TO_UDINT := DWORD_BCD_TO_UDINT + 100_0000*(BCD MOD 16); + BCD := BCD / 16; + DWORD_BCD_TO_UDINT := DWORD_BCD_TO_UDINT + 1000_0000*BCD; +END_FUNCTION + + + + +FUNCTION LWORD_BCD_TO_ULINT: ULINT + VAR_INPUT + BCD : LWORD; + END_VAR + + LWORD_BCD_TO_ULINT := DWORD_BCD_TO_UDINT(BCD MOD (256*256*256*256)); + BCD := BCD / (256*256*256*256); + LWORD_BCD_TO_ULINT := LWORD_BCD_TO_ULINT + 1_0000_0000*DWORD_BCD_TO_UDINT(BCD); +END_FUNCTION diff -r 000000000000 -r fb772792efd1 lib/bistable.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/bistable.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,65 @@ +(* + * (c) 26 Feb 2001 David Campbell + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + + +(* + * This is part of the library conatining the functions + * and function blocks defined in the standard. + * + * SR and RS function blocks + * ------------------------- + *) + + +FUNCTION_BLOCK SR + VAR_INPUT + S1 : BOOL; + R : BOOL; + END_VAR + VAR_OUTPUT + Q1 : BOOL; + END_VAR + VAR + Q_INTERNAL : BOOL; + END_VAR + + Q_INTERNAL := S1 OR ( Q_INTERNAL AND (NOT R)); + Q1 := Q_INTERNAL; +END_FUNCTION_BLOCK + +FUNCTION_BLOCK RS + VAR_INPUT + S : BOOL; + R1 : BOOL; + END_VAR + VAR_OUTPUT + Q1 : BOOL; + END_VAR + VAR + Q_INTERNAL : BOOL; + END_VAR + + Q_INTERNAL := (NOT R1) AND (S OR Q_INTERNAL); + Q1 := Q_INTERNAL; +END_FUNCTION_BLOCK diff -r 000000000000 -r fb772792efd1 lib/counter.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/counter.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,94 @@ +(* Following taken directly from the IEC 61131.3 draft standard *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + + +(* + * This is part of the library conatining the functions + * and function blocks defined in the standard. + * + * Counter function blocks + * ----------------------- + *) + + +FUNCTION_BLOCK CTU + VAR_INPUT + CU : BOOL; + R : BOOL; + PV : INT; + END_VAR + VAR_OUTPUT + Q : BOOL; + CV : INT; + END_VAR + VAR RETAIN + PVmax : INT; + END_VAR + IF R THEN CV := 0 ; + ELSIF CU AND (CV < PVmax) + THEN CV := CV+1; + END_IF ; + Q := (CV >= PV) ; +END_FUNCTION_BLOCK + + +FUNCTION_BLOCK CTD + VAR_INPUT + CD : BOOL; + LD : BOOL; + PV : INT; + END_VAR + VAR_OUTPUT + Q : BOOL; + CV : INT; + END_VAR + VAR RETAIN + PVmin : INT; + END_VAR + IF LD THEN CV := PV ; + ELSIF CD AND (CV > PVmin) + THEN CV := CV-1; + END_IF ; + Q := (CV <= 0) ; +END_FUNCTION_BLOCK + + +FUNCTION_BLOCK CTUD + VAR_INPUT + CU : BOOL; + CD : BOOL; + R : BOOL; + LD : BOOL; + PV : INT; + END_VAR + VAR_OUTPUT + QU : BOOL; + QD : BOOL; + CV : INT; + END_VAR + VAR RETAIN + PVmax : INT; + PVmin : INT; + END_VAR + IF R THEN CV := 0 ; + ELSIF LD THEN CV := PV ; + ELSE + IF NOT (CU AND CD) THEN + IF CU AND (CV < PVmax) + THEN CV := CV+1; + ELSIF CD AND (CV > PVmin) + THEN CV := CV-1; + END_IF; + END_IF; + END_IF ; + QU := (CV >= PV) ; + QD := (CV <= 0) ; +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 lib/edge_detection.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/edge_detection.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,35 @@ +(* Following taken directly from the IEC 61131.3 draft standard *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + + +(* + * This is part of the library conatining the functions + * and function blocks defined in the standard. + * + * Edge detection function blocks + * ------------------------------ + *) + +FUNCTION_BLOCK R_TRIG + VAR_INPUT CLK: BOOL; END_VAR + VAR_OUTPUT Q: BOOL; END_VAR + VAR RETAIN M: BOOL; END_VAR +Q := CLK AND NOT M; +M := CLK; +END_FUNCTION_BLOCK + +FUNCTION_BLOCK F_TRIG + VAR_INPUT CLK: BOOL; END_VAR + VAR_OUTPUT Q: BOOL; END_VAR + VAR RETAIN M: BOOL; END_VAR +Q := NOT CLK AND NOT M; +M := NOT CLK; +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 lib/ieclib.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/ieclib.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,36 @@ +(* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + +(* + * This is the library conatining the functions + * and function blocks defined in the standard. + *) + +{#include "bcd_to_int.txt" } +{#include "int_to_bcd.txt" } +{#include "edge_detection.txt" } +{#include "bistable.txt" } +{#include "counter.txt" } +{#include "timer.txt" } +{#include "time_math.txt" } diff -r 000000000000 -r fb772792efd1 lib/int_to_bcd.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/int_to_bcd.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,117 @@ +(* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + +(* + * This is part of the library conatining the functions + * and function blocks defined in the standard. + * + * Conversion to BCD + * ----------------- + *) + + + + +FUNCTION USINT_TO_BCD_BYTE: BYTE + VAR_INPUT + VALUE : USINT; + END_VAR + + USINT_TO_BCD_BYTE := VALUE MOD 10; + VALUE := VALUE / 10; + USINT_TO_BCD_BYTE := USINT_TO_BCD_BYTE + 16*VALUE; + IF VALUE > 9 + THEN + (* The standard is silent on how to handle overflows!! *) + (* We simply set it to the highets value! *) + USINT_TO_BCD_BYTE := 16#99; + END_IF; +END_FUNCTION + + + + +FUNCTION UINT_TO_BCD_WORD: WORD + VAR_INPUT + VALUE : UINT; + END_VAR + + UINT_TO_BCD_WORD := VALUE MOD 10; + VALUE := VALUE / 10; + UINT_TO_BCD_WORD := UINT_TO_BCD_WORD + 16*(VALUE MOD 10); + VALUE := VALUE / 10; + UINT_TO_BCD_WORD := UINT_TO_BCD_WORD + 16*16*(VALUE MOD 10); + VALUE := VALUE / 10; + UINT_TO_BCD_WORD := UINT_TO_BCD_WORD + 16*16*16*VALUE; + IF VALUE > 9 + THEN + (* The standard is silent on how to handle overflows!! *) + (* We simply set it to the highets value! *) + UINT_TO_BCD_WORD := 16#9999; + END_IF; +END_FUNCTION + + + + +FUNCTION UDINT_TO_BCD_DWORD: DWORD + VAR_INPUT + VALUE : UINT; + END_VAR + + UDINT_TO_BCD_DWORD := VALUE MOD 16; + VALUE := VALUE / 10; + UDINT_TO_BCD_DWORD := UDINT_TO_BCD_DWORD + 16*(VALUE MOD 10); + VALUE := VALUE / 10; + UDINT_TO_BCD_DWORD := UDINT_TO_BCD_DWORD + 256*(VALUE MOD 10); + VALUE := VALUE / 10; + UDINT_TO_BCD_DWORD := UDINT_TO_BCD_DWORD + 16*256*(VALUE MOD 10); + VALUE := VALUE / 10; + UDINT_TO_BCD_DWORD := UDINT_TO_BCD_DWORD + 256*256*(VALUE MOD 10); + VALUE := VALUE / 10; + UDINT_TO_BCD_DWORD := UDINT_TO_BCD_DWORD + 16*256*256*(VALUE MOD 10); + VALUE := VALUE / 10; + UDINT_TO_BCD_DWORD := UDINT_TO_BCD_DWORD + 256*256*256*(VALUE MOD 10); + VALUE := VALUE / 10; + UDINT_TO_BCD_DWORD := UDINT_TO_BCD_DWORD + 16*256*256*256*VALUE; + IF VALUE > 9 + THEN + (* The standard is silent on how to handle overflows!! *) + (* We simply set it to the highest value! *) + UDINT_TO_BCD_DWORD := 16#9999_9999; + END_IF; +END_FUNCTION + + + + +FUNCTION ULINT_TO_BCD_LWORD: LWORD + VAR_INPUT + VALUE : UINT; + END_VAR + + ULINT_TO_BCD_LWORD := UDINT_TO_BCD_DWORD(VALUE MOD 1_0000_0000); + VALUE := VALUE / 1_0000_0000; + ULINT_TO_BCD_LWORD := ULINT_TO_BCD_LWORD + 256*256*256*256*UDINT_TO_BCD_DWORD(VALUE); +END_FUNCTION diff -r 000000000000 -r fb772792efd1 lib/ramp_st.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/ramp_st.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,25 @@ + FUNCTION_BLOCK RAMP + VAR_INPUT + RUN : BOOL ; (* 0 - track X0, 1 - ramp to/track X1 *) + X0,X1 : REAL ; + TR : TIME ; (* Ramp duration *) + CYCLE : TIME ; (* Sampling period *) + END_VAR + VAR_OUTPUT + BUSY : BOOL ; (* BUSY = 1 during ramping period *) + XOUT : REAL := 0.0 ; + END_VAR + VAR XI : REAL ; (* Initial value *) + T : TIME := T#0s; (* Elapsed time of ramp *) + END_VAR + BUSY := RUN ; + IF RUN THEN + IF T >= TR THEN BUSY := 0 ; XOUT := X1 ; + ELSE XOUT := XI + (X1-XI) * TIME_TO_REAL(T) + / TIME_TO_REAL(TR) ; + T := T + CYCLE ; + END_IF ; + ELSE XOUT := X0 ; XI := X0 ; T := t#0s ; + END_IF ; + END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 lib/time_math.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/time_math.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,360 @@ +(* + * (c) 2005 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + +(* + * This is part of the library conatining the functions + * and function blocks defined in the standard. + * + * Math functions on Time and Date data types. + * ------------------------------------------- + * + * We also include the two data type conversions defined in table 30 of the IEC 61131-3 standard (version + *) + + +(* NOTE: The IEC 61131-3 standard library includes overloaded functions, which are not + * allowed in user code. Since our compiler does not yet support overloaded functions, these + * are commented out in the following library for the moment... + *) + +(***************) +(* TIME + TIME *) +(***************) + +FUNCTION ADD : TIME + VAR_INPUT + IN1 : TIME; + IN2 : TIME; + END_VAR + + ADD := IN1 + IN2; +END_FUNCTION + + +FUNCTION ADD_TIME : TIME + VAR_INPUT + IN1 : TIME; + IN2 : TIME; + END_VAR + + ADD_TIME := IN1 + IN2; +END_FUNCTION + + +FUNCTION SUB : TIME + VAR_INPUT + IN1 : TIME; + IN2 : TIME; + END_VAR + + SUB := IN1 - IN2; +END_FUNCTION + + +FUNCTION SUB_TIME : TIME + VAR_INPUT + IN1 : TIME; + IN2 : TIME; + END_VAR + + SUB_TIME := IN1 - IN2; +END_FUNCTION + + + +(**************) +(* TIME + TOD *) +(**************) + +FUNCTION ADD : TOD + VAR_INPUT + IN1 : TOD; + IN2 : TIME; + END_VAR + + ADD := IN1 + IN2; +END_FUNCTION + + +(* The following function is not defined in the standard, but its existance makes sense. *) +FUNCTION ADD : TOD + VAR_INPUT + IN1 : TIME; + IN2 : TOD; + END_VAR + + ADD := IN1 + IN2; +END_FUNCTION + + +FUNCTION ADD_TOD_TIME : TOD + VAR_INPUT + IN1 : TOD; + IN2 : TIME; + END_VAR + + ADD_TOD_TIME := IN1 + IN2; +END_FUNCTION + + +FUNCTION SUB : TOD + VAR_INPUT + IN1 : TOD; + IN2 : TIME; + END_VAR + + SUB := IN1 - IN2; +END_FUNCTION + + +FUNCTION SUB_TOD_TIME : TOD + VAR_INPUT + IN1 : TOD; + IN2 : TIME; + END_VAR + + SUB_TOD_TIME := IN1 - IN2; +END_FUNCTION + + + +(*************) +(* TIME + DT *) +(*************) + +FUNCTION ADD : DT + VAR_INPUT + IN1 : DT; + IN2 : TIME; + END_VAR + + ADD := IN1 + IN2; +END_FUNCTION + + +(* The following function is not defined in the standard, but its existance makes sense. *) +FUNCTION ADD : DT + VAR_INPUT + IN1 : TIME; + IN2 : DT; + END_VAR + + ADD := IN1 + IN2; +END_FUNCTION + + +FUNCTION ADD_DT_TIME : DT + VAR_INPUT + IN1 : DT; + IN2 : TIME; + END_VAR + + ADD_DT_TIME := IN1 + IN2; +END_FUNCTION + + + +(***************) +(* DATE + DATE *) +(***************) + +FUNCTION SUB : TIME + VAR_INPUT + IN1 : DATE; + IN2 : DATE; + END_VAR + + SUB := IN1 - IN2; +END_FUNCTION + + +FUNCTION SUB_DATE_DATE : TIME + VAR_INPUT + IN1 : DATE; + IN2 : DATE; + END_VAR + + SUB_DATE_DATE := IN1 - IN2; +END_FUNCTION + + + +(*************) +(* TOD + TOD *) +(*************) + +FUNCTION SUB : TIME + VAR_INPUT + IN1 : TOD; + IN2 : TOD; + END_VAR + + SUB := IN1 - IN2; +END_FUNCTION + + +FUNCTION SUB_TOD_TOD : TIME + VAR_INPUT + IN1 : TOD; + IN2 : TOD; + END_VAR + + SUB_TOD_TOD := IN1 - IN2; +END_FUNCTION + + + +(*************) +(* TIME + DT *) +(*************) + +FUNCTION SUB : DT + VAR_INPUT + IN1 : DT; + IN2 : TIME; + END_VAR + + SUB := IN1 - IN2; +END_FUNCTION + + +FUNCTION SUB_DT_TIME : DT + VAR_INPUT + IN1 : DT; + IN2 : TIME; + END_VAR + + SUB_DT_TIME := IN1 - IN2; +END_FUNCTION + + + +(***********) +(* DT + DT *) +(***********) + +FUNCTION SUB : TIME + VAR_INPUT + IN1 : DT; + IN2 : DT; + END_VAR + + SUB := IN1 - IN2; +END_FUNCTION + + +FUNCTION SUB_DT_DT : TIME + VAR_INPUT + IN1 : DT; + IN2 : DT; + END_VAR + + SUB_DT_DT := IN1 - IN2; +END_FUNCTION + + + +(******************) +(* TIME * ANY_NUM *) +(******************) + +FUNCTION MUL : TIME + VAR_INPUT + IN1 : TIME; + IN2 : LREAL; + END_VAR + + MUL := IN1 * IN2; +END_FUNCTION + + +FUNCTION MULTIME : TIME + VAR_INPUT + IN1 : TIME; + IN2 : LREAL; + END_VAR + + MULTIME := IN1 * IN2; +END_FUNCTION + + +FUNCTION DIV : TIME + VAR_INPUT + IN1 : TIME; + IN2 : LREAL; + END_VAR + + DIV := IN1 / IN2; +END_FUNCTION + + +FUNCTION DIVTIME : TIME + VAR_INPUT + IN1 : TIME; + IN2 : LREAL; + END_VAR + + DIVTIME := IN1 / IN2; +END_FUNCTION + + + +(*************) +(* DATE + DT *) +(*************) + +FUNCTION CONCAT_DATE_TOD : DT + VAR_INPUT + IN1 : DATE; + IN2 : TOD; + END_VAR + + CONCAT_DATE_TOD := IN1 + IN2; +END_FUNCTION + + + +(*************************) +(* Data Type Conversions *) +(*************************) + +FUNCTION DT_TO_DATE : DATE + VAR_INPUT + IN1 : DT; + END_VAR + + {DT_TO_DATE = IN1.__to_DATE();} +END_FUNCTION + + + +FUNCTION DT_TO_TOD : TOD + VAR_INPUT + IN1 : DT; + END_VAR + + {DT_TO_TOD = IN1.__to_TOD();} +END_FUNCTION + + diff -r 000000000000 -r fb772792efd1 lib/timer.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/timer.txt Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,280 @@ +(* + * (c) 2005 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + *) + +(* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + *) + +(* + * This is part of the library conatining the functions + * and function blocks defined in the standard. + * + * Timer Function Blocks + * --------------------- + * + * NOTE: The timing diagrams in the comments were taken from the + * IEC 61131-3 standard. + *) + + + +(**************************************************************** + + TP - pulse timing - state machine + + + +--------+ ++ ++ +--------+ + IN | | || || | | + --+ +-----++-++---+ +--------- + t0 t1 t2 t3 t4 t5 + +----+ +----+ +----+ + Q | | | | | | + --+ +---------+ +--+ +------------- + t0 t0+PT t2 t2+PT t4 t4+PT + PT +---+ + +---+ + : / | /| / | + ET : / | / | / | + : / | / | / | + : / | / | / | + 0-+ +-----+ +--+ +--------- + t0 t1 t2 t4 t5 + + + 2 +---+ + +---+ +STATE 1 +----+ | +----| +----+ | + 0 --+ +-----+ +--+ +--------- + + +****************************************************************) + +FUNCTION_BLOCK TP + VAR_INPUT + IN : BOOL; (* first input parameter *) + PT : TIME; (* second input parameter *) + END_VAR + VAR_OUTPUT + Q : BOOL := FALSE; (* first output parameter *) + ET : TIME := T#0s; (* second output parameter *) + END_VAR + + VAR + STATE : SINT := 0; (* internal state: 0-reset, 1-counting, 2-set *) + PREV_IN : BOOL := FALSE; + CURRENT_TIME, START_TIME : TIME; + END_VAR + + {data__->CURRENT_TIME = __CURRENT_TIME;} + + IF ((STATE = 0) AND NOT(PREV_IN) AND IN) (* found rising edge on IN *) + THEN + (* start timer... *) + STATE := 1; + Q := TRUE; + START_TIME := CURRENT_TIME; + + ELSIF (STATE = 1) + THEN + IF ((START_TIME + PT) <= CURRENT_TIME) + THEN + STATE := 2; + Q := FALSE; + ET := PT; + ELSE + ET := CURRENT_TIME - START_TIME; + END_IF; + END_IF; + + IF ((STATE = 2) AND NOT(IN)) + THEN + ET := T#0s; + STATE := 0; + END_IF; + + + PREV_IN := IN; + +END_FUNCTION_BLOCK + + + + +(**************************************************************** + + TON - On-delay timing - state machine + + + +--------+ +---+ +--------+ + IN | | | | | | + --+ +--------+ +---+ +------------- + t0 t1 t2 t3 t4 t5 + +---+ +---+ + Q | | | | + -------+ +---------------------+ +------------- + t0+PT t1 t4+PT t5 + PT +---+ +---+ + : / | + / | + ET : / | /| / | + : / | / | / | + : / | / | / | + 0-+ +--------+ +---+ +------------- + t0 t1 t2 t3 t4 t5 + + + 2 +---+ +---+ +STATE 1 +----+ | +---+ +----+ | + 0 --+ +--------+ +---+ +------ + + +****************************************************************) + + +FUNCTION_BLOCK TON + VAR_INPUT + IN : BOOL; (* first input parameter *) + PT : TIME; (* second input parameter *) + END_VAR + VAR_OUTPUT + Q : BOOL := FALSE; (* first output parameter *) + ET : TIME := T#0s; (* second output parameter *) + END_VAR + + VAR + STATE : SINT := 0; (* internal state: 0-reset, 1-counting, 2-set *) + PREV_IN : BOOL := FALSE; + CURRENT_TIME, START_TIME : TIME; + END_VAR + + {data__->CURRENT_TIME = __CURRENT_TIME;} + + IF ((STATE = 0) AND NOT(PREV_IN) AND IN) (* found rising edge on IN *) + THEN + (* start timer... *) + STATE := 1; + Q := FALSE; + START_TIME := CURRENT_TIME; + + ELSE + (* STATE is 1 or 2 !! *) + IF (NOT(IN)) + THEN + ET := T#0s; + Q := FALSE; + STATE := 0; + + ELSIF (STATE = 1) + THEN + IF ((START_TIME + PT) <= CURRENT_TIME) + THEN + STATE := 2; + Q := TRUE; + ET := PT; + ELSE + ET := CURRENT_TIME - START_TIME; + END_IF; + END_IF; + + END_IF; + + PREV_IN := IN; + +END_FUNCTION_BLOCK + + + + + +(**************************************************************** + + TOFF - Off-delay timing - state machine + + + +--------+ +---+ +--------+ + IN | | | | | | + ---+ +--------+ +---+ +----------- + t0 t1 t2 t3 t4 t5 + +-------------+ +---------------------+ + Q | | | | + ---+ +---+ +------ + t0 t1+PT t2 t5+PT + PT +---+ +------ + : / | + / + ET : / | /| / + : / | / | / + : / | / | / + 0------------+ +---+ +--------+ + t1 t3 t5 + + + 2 +---+ +------ +STATE 1 +----+ | +---+ +----+ + 0 -------------+ +---+ +--------+ + + +****************************************************************) + +FUNCTION_BLOCK TOFF + VAR_INPUT + IN : BOOL; (* first input parameter *) + PT : TIME; (* second input parameter *) + END_VAR + VAR_OUTPUT + Q : BOOL := FALSE; (* first output parameter *) + ET : TIME := T#0s; (* second output parameter *) + END_VAR + + VAR + STATE : SINT := 0; (* internal state: 0-reset, 1-counting, 2-set *) + PREV_IN : BOOL := FALSE; + CURRENT_TIME, START_TIME : TIME; + END_VAR + + {data__->CURRENT_TIME = __CURRENT_TIME;} + + IF ((STATE = 0) AND PREV_IN AND NOT(IN)) (* found falling edge on IN *) + THEN + (* start timer... *) + STATE := 1; + START_TIME := CURRENT_TIME; + + ELSE + (* STATE is 1 or 2 !! *) + IF (IN) + THEN + ET := T#0s; + STATE := 0; + + ELSIF (STATE = 1) + THEN + IF ((START_TIME + PT) <= CURRENT_TIME) + THEN + STATE := 2; + ET := PT; + ELSE + ET := CURRENT_TIME - START_TIME; + END_IF; + END_IF; + + END_IF; + + Q := IN OR (STATE = 1); + PREV_IN := IN; + +END_FUNCTION_BLOCK + diff -r 000000000000 -r fb772792efd1 main.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,133 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + **************************************************************** + **************************************************************** + **************************************************************** + ********* ********* + ********* ********* + ********* O V E R A L L A R C H I T E C T U R E ********* + ********* ********* + ********* ********* + **************************************************************** + **************************************************************** + **************************************************************** + + The compiler works in 4(+1) stages: + Stage 1 - Lexical analyser - implemented with flex (iec.flex) + Stage 2 - Syntax parser - implemented with bison (iec.y) + Stage 3 - Semantics analyser - not yet implemented + Stage 4 - Code generator - implemented in C++ + Stage 4+1 - Binary code generator - gcc, javac, etc... + + + Data structures passed between stages, in global variables: + 1->2 : tokens (int), and token values (char *) + 2->1 : symbol tables (defined in symtable.hh) + 2->3 : abstract syntax tree (tree of C++ classes, in absyntax.hh file) + 3->4 : Same as 2->3 + 4->4+1 : file with program in c, java, etc... + + + The compiler works in several passes: + Pass 1: executes stages 1 and 2 simultaneously + Pass 2: executes stage 3 + Pass 3: executes stage 4 + Pass 4: executes stage 4+1 +*/ + + + +//#include // printf() + +#include // EXIT_FAILURE +#include "absyntax/absyntax.hh" // symbol_c type + + + + +/* A macro for printing out internal parser errors... */ +#include // required for std::cerr +#define ERROR error_exit(__FILE__,__LINE__) +void error_exit(const char *file_name, int line_no) { + std::cerr << "\nInternal program error in file " << file_name + << " at line " << line_no << "\n\n\n"; + exit(EXIT_FAILURE); +} + + + +/* forward declarations... */ +int stage1_2(const char *filename, const char *includedir, symbol_c **tree_root); +//int stage3(symbol_c *tree_root); +int stage4(symbol_c *tree_root); + + +static void printusage(const char *cmd) { + printf("%s [] [-I ]\n", cmd); +} + + + +int main(int argc, char **argv) { + symbol_c *tree_root; + char * includedir = NULL; + + if (argc == 4) { + if (strcmp(argv[2], "-I") != 0) { + printusage(argv[0]); + return EXIT_FAILURE; + } + includedir = argv[3]; + argc = 2; + } + + if (argc != 2) { + printusage(argv[0]); + return EXIT_FAILURE; + } + + /* 1st Pass */ + if (stage1_2(argv[1], includedir, &tree_root) < 0) + return EXIT_FAILURE; + + /* 2nd Pass */ + /* not yet implemented... */ + /* + if (stage3(tree_root) < 0) + return EXIT_FAILURE; + */ + + /* 3rd Pass */ + if (stage4(tree_root) < 0) + return EXIT_FAILURE; + + /* 4th Pass */ + /* Currently implemented in the Makefile! */ + + return 0; +} + + diff -r 000000000000 -r fb772792efd1 readme --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/readme Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,407 @@ + + + + IEC 61131-3 IL and ST compiler + + + The following compiler has been based on the + FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + + + (c) 2003 Mario de Sousa + + + +**************************************************************** +**************************************************************** +**************************************************************** +********* ********* +********* ********* +********* O V E R A L L A R C H I T E C T U R E ********* +********* ********* +********* ********* +**************************************************************** +**************************************************************** +**************************************************************** + + The compiler works in 4(+1) stages: + Stage 1 - Lexical analyser - implemented with flex (iec.flex) + Stage 2 - Syntax parser - implemented with bison (iec.y) + Stage 3 - Semantics analyser - not yet implemented + Stage 4 - Code generator - implemented in C++ + Stage 4+1 - Binary code generator - gcc, javac, etc... + + + Data structures passed between stages, in global variables: + 1->2 : tokens (int), and token values (char *) + 2->1 : symbol tables (defined in symtable.hh) + 2->3 : abstract syntax tree (tree of C++ classes, in absyntax.hh file) + 3->4 : Same as 2->3 + 4->4+1 : file with program in c, java, etc... + + + The compiler works in several passes: + Pass 1: executes stages 1 and 2 simultaneously + Pass 2: executes stage 3 + Pass 3: executes stage 4 + Pass 4: executes stage 4+1 + + + NOTE 1 + ====== + Note that stage 2 passes data back to stage 1. This is only +possible because both stages are executed in the same pass. + + + + NOTE 2 + ====== + I (Mario) have a feeling that the abstract syntax may be +considerably simplified without any drawbacks to semantic checking +and code generation. I have nevertheless opted to keep as much +info as possible in the abstract syntax tree, in case it may become +necessary further on. + Once we start coding the next stages (semantic checking and code +generation) I will get a better understanding of what is required +of the abstract syntax tree. At that stage I will be better +positioned to make a more informed decision on how best to structure +the abstract syntax tree. + For now, we play conservative and keep as much info as possible. + + + + NOTE 3 + ====== + It would be nice to get this parser integrated into the gcc +group of compilers. We would then be able to compile our st/il +programs directly into executable binaries, for all the processor +architectures gcc currently supports. + The gcc compilers are divided into a frontend and backend. The +data structure between these two stages is called the syntax +tree. In essence, we would need to create a new frontend that +would parse the st/il program and build the syntax tree. +Unfortunately the gcc syntax tree is not very well documented, +and doing semantic checking on this tree would probably be a +nightmare. + We therefore chose to follow the same route as the gnat (ada 95) +and cobol compilers, i.e. generate our own abstract syntax tree, +do semantic checking on our tree, do whatever optimisation +we can at this level on our own tree, and only then build +the gcc syntax tree from our abstract syntax tree. + All this may still be integrated with the gcc backend to generate +a new gnu compiler for the st and il programming languages. +Since generating the gcc syntax tree will probably envolve some +trial and error effort due to the sparseness of documentation, +we chose to start off by coding a C++ code generator for +our stage 4. We may later implement a gcc syntax tree generator +as an alternative stage 4 process, and then integrate it with +the gcc toplevel.c file (command line parsing, etc...). + + + +**************************************************************** +**************************************************************** +**************************************************************** +********* ********* +********* ********* +********* S T A G E 1 ********* +********* ********* +********* ********* +**************************************************************** +**************************************************************** +**************************************************************** + + + +Issue 1 +======= + + The syntax defines the common_character_representation as: + | + + Flex includes the function print_char() that defines +all printable characters portably (i.e. whatever character +encoding is currently being used , ASCII, EBCDIC, etc...) +Unfortunately, we cannot generate the definition of +common_character_representation portably, since flex +does not allow definition of sets by subtracting +elements in one set from another set (Note how +common_character_representation could be defined by +subtracting '$' '"' and "'" from print_char() ). +This means we must build up the defintion of +common_character_representation using only set addition, +which leaves us with the only choice of defining the +characters non-portably... + + In short, the definition we use for common_character_representation +only works for ASCII character encoding! + + + + +Issue 2 +======= + +We extend the IEC 61131-3 standard syntax to allow inclusion of +other files. The accepted syntax is: + + (*#include "" *) + +Note how this would be ignored by other standard complient compilers +as a simple comment! + + + +**************************************************************** +**************************************************************** +**************************************************************** +********* ********* +********* ********* +********* S T A G E 2 ********* +********* ********* +********* ********* +**************************************************************** +**************************************************************** +**************************************************************** + + Overall Comments + ================ + + + Comment 1 + --------- + We have augmented the syntax the specification defines to include +restrictions defined in the semantics of the languages. + + This is required because the syntax cannot be parsed by a LALR(1) +parser as it is presented in the specification. Many reduce/reduce +and shift/reduce conflicts arise. This is mainly because the parser +cannot discern how to reduce an identifier. Identifiers show up in +many places in the syntax, and it is not entirely possible to +figure out if the identifier is a variable_name, enumeration +value, function block name, etc... only from the context in +which it appears. + + A more detailed example of why we need symbol tables are +the type definitions... In definition of new types +(section B 1.3.3) the parser needs to figure out the class of +the new type being defined (enumerated, structure, array, etc...). +This works well when the base classes are elementary types +(or structures, enumeration, arrays, etc. thereof). It becomes +confusing to the parser when the new_type is based on a previously +user defined type. + +TYPE + new_type_1 : INT := 99; + new_type_2 : new_type_1 := 100; +END_TYPE + + When parsing new_type_1, the parser can figure out that the +identifier new_type_1 is a simple_type_name, because it is +based on a elementary type without structure, arrays, etc... + While parsing new_type_2, it becomes confused how to reduce +the new_type_2 identifier, as it is based on the identifier +new_type_1, of which it does not know the class (remember, at this +stage new_type_1 is a simple identifier!). + We therefore need to keep track of the class of the user +defined types as they are declared, so that the lexical analyser +can tell the syntax parser what class the type belongs to. We +cannot use the abstract syntax tree itself to search for the +declaration of new_type_1 as we only get a handle to the root +of the tree at the end of the parsing. + + We therefore maintain an independent and parallel table of symbols, +that is filled as we come across the type delcarations in the code. +Actually, we ended up also needing to store variable names in +the symbol table. Since variable names come and go out of scope +depending on what portion of code we are parsing, we sometimes +need to remove the variable names from the symbol table. +Since the ST and IL languages only have a single level of scope, +I (Mario) found it easier to simply use a second symbol table for +the variable names that is completely cleared when the parser +reaches the end of a function (function block or program). + +What I mean when I say that these languages have a single level +of scope is that all variables used in a function (function block +or program) must be declared inside that function (function block +or program). Even global variables must be re-declared as EXTERN +before a function may access them! This means that it is easy +to simply load up the variable name symbol table when we start +parsing a function (function block or program), and to clear it +when we reach the end. Checking whether variables declared +as EXTERN really exist inside a RESOURCE or a CONFIGURATION +is left to stage 3 (semantic checking) where we can use the +abstract tree itself to search for the variables (NOTE: semantic +cheching at stage 3 has not yet been implemented, so we may yet +end up using a symbol table too at that stage!). + + Due to the use of the symbol tables, and special identifier +tokens depending on the type of identifier it had previously +been declared in the code being parsed, the syntax was slightly +changed regarding the definition of variable names, derived +function names, etc... FROM for e.g.: +variable_name: identifier; +TO +variable_name: variable_name_token; + + Flex first looks at the symbol tables when it finds an identifier, +and returns the correct token corresponding to the identifier +type in question. Only if the identifier is not currently stored +in any symbol table, does flex return a simple identifier_token. + + This means that the declarations of variables, functions etc... +were changed FROM: +function_declaration: FUNCTION derived_function_name ... +TO +function_declaration: FUNCTION identifier ... +since the initial definition of derived_function_name had been +changed FROM +derived_function_name: identifier; +TO +derived_function_name: derived_function_name_token; + + + + + Comment 2 + --------- + Since the ST and IL languages share a lot of common syntax, +I have decided to write a single parser to handle both languages +simultaneously. This approach has the advantage that the user +may mix the language used in the same file, as long as each function +is written in a single lanuage. + + This approach also assumes that all the IL language operators are +keywords, which means that it is not possible to define variables +using names such as "LD", "ST", etc... +Note that the spec does not consider these operators to be keywords, +so it means that they should be available for variable names! On the +other hand, all implementations of the ST and IL languages seems to +treat them as keywords, so there is not much harm in doing the same. + + If it ever becomes necessary to allow variables with names of IL +operators, either the syntax will have to be augmented, or we can +brake up the parser in two: one for ST and another for IL. + + + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + +Issue 1 +======= + +According to the spec, the valid construct +TYPE new_str_type : STRING := "hello!"; END_TYPE +has two possible routes to type_declaration... + +Route 1: +type_declaration: single_element_type_declaration +single_element_type_declaration: simple_type_declaration +simple_type_declaration: identifier ':' simple_spec_init +simple_spec_init: simple_specification ASSIGN constant +(shift: identifier <- 'new_str_type') +simple_specification: elementary_type_name +elementary_type_name: STRING +(shift: elementary_type_name <- STRING) +(reduce: simple_specification <- elementary_type_name) +(shift: constant <- "hello!") +(reduce: simple_spec_init: simple_specification ASSIGN constant) +(reduce: ...) + + +Route 2: +type_declaration: string_type_declaration +string_type_declaration: identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init +(shift: identifier <- 'new_str_type') +elementary_string_type_name: STRING +(shift: elementary_string_type_name <- STRING) +(shift: string_type_declaration_size <- /* empty */) +string_type_declaration_init: ASSIGN character_string +(shift: character_string <- "hello!") +(reduce: string_type_declaration_init <- ASSIGN character_string) +(reduce: string_type_declaration <- identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init ) +(reduce: type_declaration <- string_type_declaration) + + + At first glance it seems that removing route 1 would make +the most sense. Unfortunately the construct 'simple_spec_init' +shows up multiple times in other rules, so changing this construct +would mean changing all the rules in which it appears. +I (Mario) therefore chose to remove route 2 instead. This means +that the above declaration gets stored in a +simple_type_declaration_c, and not in a string_type_declaration_c +as would be expected! + + +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ + + +Issue 1 +======= + + Due to reduce/reduce conflicts between identifiers +being reduced to either a variable or an enumerator value, +we were forced to keep a symbol table of the names +of all declared variables. Variables are no longer +created from simple identifier_token, but from +variable_name_token. + + BUT, in functions the function name may be used as +a variable! In order to be able to parse this correctly, +the token parser (flex) must return a variable_name_token +when it comes across the function name, while parsing +the function itself. +We do this by inserting the function name into the variable +symbol table, and having flex return a variable_name_token +whenever it comes across it. +When we finish parsing the function the variable name +symbol table is cleared of all entries, and the function +name is inserted into the library element symbol table. This +means that from then onwards flex will return a +derived_function_name_token whenever it comes across the +function name. + +In order to insert the function name into the variable_name +symbol table BEFORE the function body gets parsed, we +need the parser to reduce a construct that contains the +the function name. That is why we created the extra +construct 'function_name_declaration', i.e. to force +the parser to reduce it, before parsing the function body, +and therefore get an oportunity to insert the function name +into the variable name symbol table! + + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ + +Issue 1 +======= + +For the 'FOR' iteration loop + + FOR control_variable ASSIGN expression TO expression BY expression DO statement_list END_FOR + +The spec declares the control variable in the syntax as + + control_variable: identifier; + +but then defines the semantics of control_variable (Section 3.3.2.4) +as being of an integer type (e.g., SINT, INT, or DINT). +Obviously this presuposes that the control_variable must have been +declared in some 'VAR .. VAR_END' construct, so I (Mario) changed +the syntax to read + + control_variable: variable_name; + + + + + +************************************************************************** + + (c) 2003 Mario de Sousa diff -r 000000000000 -r fb772792efd1 stage1_2/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage1_2/Makefile Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,49 @@ +# include the system specific Makefile +#include ../../../Makefile.$(shell uname) + + + +default: all + +.PHONY: iec.flex + +all: iec.flex.o iec.y.o + +clean: + -rm -f *.o Makefile.depend + -rm -f iec.flex.c iec.y.cc iec.y.hh iec.y.output + -rm -f test_flex + +#get warnings, debugging information and optimization +CFLAGS = -Wall -pedantic -Wpointer-arith -Wwrite-strings +# CFLAGS += -Werror + +CFLAGS += -ggdb -O3 -funroll-loops +# Note: if the optimizer crashes, we'll leave out the -O3 for those files + +CFLAGS += -I. -I../* -I../../absyntax + +test_flex: iec.flex.c iec.y.hh + $(CXX) -o test_flex.o -c iec.flex.c -DTEST_MAIN $(CFLAGS) + $(CXX) -o test_flex test_flex.o +# $(CXX) -o test_flex test_flex.o ../util/symtable.o -DTEST_MAIN + +iec.flex.c: iec.flex + flex -oiec.flex.c iec.flex + +iec.flex.o: iec.y.hh iec.flex.c + $(CXX) -c iec.flex.c -D LIBDIRECTORY='"$(IECLIBDIR)"' $(CFLAGS) + +iec.y.hh iec.y.cc: iec.y + bison -d -v -o iec.y.cc iec.y + +iec.y.o: iec.y.cc iec.y.hh + $(CXX) -c iec.y.cc -D LIBDIRECTORY='"$(IECLIBDIR)"' $(CFLAGS) + + + + + +#how to make things from other directories if they are missing +../% /%: + $(MAKE) -C $(@D) $(@F) diff -r 000000000000 -r fb772792efd1 stage1_2/iec.flex --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage1_2/iec.flex Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,1384 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + * Stage 1 + * ======= + * + * This file contains the lexical tokens definitions, from which + * the flex utility will generate a lexical parser function. + */ + + + + +/*****************************/ +/* Lexical Parser Options... */ +/*****************************/ + +/* The lexical analyser will never work in interactive mode, + * i.e., it will only process programs saved to files, and never + * programs being written inter-actively by the user. + * This option saves the resulting parser from calling the + * isatty() function, that seems to be generating some compile + * errors under some (older?) versions of flex. + */ +%option never-interactive + +/* Have the lexical analyser use a 'char *yytext' instead of an + * array of char 'char yytext[??]' to store the lexical token. + */ +%pointer + + +/* Have the lexical analyser ignore the case of letters. + * This will occur for all the tokens and keywords, but + * the resulting text handed up to the syntax parser + * will not be changed, and keep the original case + * of the letters in the input file. + */ +%option case-insensitive + +/* Have the generated lexical analyser keep track of the + * line number it is currently analysing. + * This is used to pass up to the syntax parser + * the number of the line on which the current + * token was found. It will enable the syntax parser + * to generate more informatve error messages... + */ +%option yylineno + +/* required for the use of the yy_pop_state() and + * yy_push_state() functions + */ +%option stack + +/* The '%option stack' also requests the inclusion of + * the yy_top_state(), however this function is not + * currently being used. This means that the compiler + * is complaining about the existance of this function. + * The following option removes the yy_top_state() + * function from the resulting c code, so the compiler + * no longer complains. + */ +%option noyy_top_state + +/**************************************************/ +/* External Variable and Function declarations... */ +/**************************************************/ + + +%{ +/* Define TEST_MAIN to include a main() function. + * Useful for testing the parser generated by flex. + */ +/* +#define TEST_MAIN +*/ +/* If lexical parser is compiled by itself, we need to define the following + * constant to some string. Under normal circumstances LIBDIRECTORY is set + * in the syntax parser header file... + */ +#ifdef TEST_MAIN +#define LIBDIRECTORY "just_testing" +#endif + + + +/* Required for strdup() */ +#include + +/* Required only for the declaration of abstract syntax classes + * (class symbol_c; class token_c; class list_c;) + * These will not be used in flex, but the token type union defined + * in iec.hh contains pointers to these classes, so we must include + * it here. + */ +#include "../absyntax/absyntax.hh" + +/* generated by bison. + * Contains the definition of the token constants, and the + * token value type YYSTYPE (in our case, a 'const char *') + */ +#include "iec.y.hh" + +/* Variable defined by the bison parser, + * where the value of the tokens will be stored + */ +extern YYSTYPE yylval; + +/* The name of the file currently being parsed... + * This variable is declared and read from the code generated by bison! + * Note that flex accesses and updates this global variable + * apropriately whenever it comes across an (*#include *) + * directive... + */ +extern const char *current_filename; + +/* We will not be using unput() in our flex code... */ +#define YY_NO_UNPUT + +/* Variable defined by the bison parser. + * It must be initialised with the location + * of the token being parsed. + * This is only needed if we want to keep + * track of the locations, in order to give + * more meaningful error messages! + */ +extern YYLTYPE yylloc; + +/* Macro that is executed for every action. + * We use it to pass the location of the token + * back to the bison parser... + */ +#define YY_USER_ACTION { \ + yylloc.first_line = yylloc.last_line = yylineno; \ + yylloc.first_column = yylloc.last_column = 0; \ + } + + +/* Since this lexical parser we defined only works in ASCII based + * systems, we might as well make sure it is being compiled on + * one... + * Lets check a few random characters... + */ +#if (('a' != 0x61) || ('A' != 0x41) || ('z' != 0x7A) || ('Z' != 0x5A) || \ + ('0' != 0x30) || ('9' != 0x39) || ('(' != 0x28) || ('[' != 0x5B)) +#error This lexical analyser is not portable to a non ASCII based system. +#endif + + +/* Function only called from within flex, but defined + * in iec.y! + * We delcare it here... + * + * Search for a symbol in either of the two symbol tables + * and return the token id of the first symbol found. + * Searches first in the variables, and only if not found + * does it continue searching in the library elements + */ +//token_id_t get_identifier_token(const char *identifier_str); +int get_identifier_token(const char *identifier_str); +%} + + +/***************************************************/ +/* Forward Declaration of functions defined later. */ +/***************************************************/ + +%{ +/* return all the text in the current token back to the input stream. */ +void unput_text(unsigned int n); +%} + + + +/****************************/ +/* Lexical Parser States... */ +/****************************/ + +/* NOTE: Our psrser can parse st or il code, intermixed + * within the same file. + * With IL we come across the issue of the EOL (end of line) token. + * ST, and the declaration parts of IL do not use this token! + * If the lexical analyser were to issue this token during ST + * language parsing, or during the declaration of data types, + * function headers, etc. in IL, the syntax parser would crash. + * + * We can solve this issue using one of three methods: + * (1) Augment all the syntax that does not accept the EOL + * token to simply ignore it. This makes the syntax + * definition (in iec.y) very cluttered! + * (2) Let the lexical parser figure out which language + * it is parsing, and decide whether or not to issue + * the EOL token. This requires the lexical parser + * to have knowledge of the syntax!, making for a poor + * overall organisation of the code. It would also make it + * very difficult to understand the lexical parser as it + * would use several states, and a state machine to transition + * between the states. The state transitions would be + * intermingled with the lexical parser defintion! + * (3) Use a mixture of (1) and (2). The lexical analyser + * merely distinguishes between function headers and function + * bodies, but no longer makes a distinction between il and + * st language bodies. When parsing a body, it will return + * the EOL token. In other states '\n' will be ignored as + * whitespace. + * The ST language syntax has been augmented in the syntax + * parser configuration to ignore any EOL tokens that it may + * come across! + * This option has both drawbacks of option (1) and (2), but + * much less intensely. + * The syntax that gets cluttered is limited to the ST statements + * (which is rather limited, compared to the function headers and + * data type declarations, etc...), while the state machine in + * the lexical parser becomes very simple. All state transitions + * can be handled within the lexical parser by itself, and can be + * easily identified. Thus knowledge of the syntax required by + * the lexical parser is very limited! + * + * Amazingly enough, I (Mario) got to implement option (3) + * at first, requiring two basic states, decl and body. + * The lexical parser will enter the body state when + * it is parsing the body of a function/program/function block. The + * state transition is done when we find a VAR_END that is not followed + * by a VAR! This is the syntax knowledge that gets included in the + * lexical analyser with this option! + * Unfortunately, getting the st syntax parser to ignore EOL anywhere + * where they might appear leads to conflicts. This is due to the fact + * that the syntax parser uses the single look-ahead token to remove + * possible conflicts. When we insert a possible EOL, the single + * look ahead token becomes the EOL, which means the potential conflicts + * could no longer be resolved. + * Removing these conflicts would make the st syntax parser very convoluted, + * and adding the extraneous EOL would make it very cluttered. + * This option was therefore dropped in favour of another! + * + * I ended up implementing (2). Unfortunately the lexical analyser can + * not easily distinguish between il and st code, since function + * calls in il are very similar to function block calls in st. + * We therefore use an extra 'body' state. When the lexical parser + * finds that last END_VAR, it enters the body state. This state + * must figure out what language is being parsed from the first few + * tokens, and switch to the correct state (st or il) according to the + * language. This means that we insert quite a bit of knowledge of the + * syntax of the languages into the lexical parser. This is ugly, but it + * works, and at least it is possible to keep all the state changes together + * to make it easier to remove them later on if need be. + * The body state returns any matched text back to the buffer with unput(), + * to be later matched correctly by the apropriate language parser (st or il). + * The state machine has 6 possible states (INITIAL, config, decl, body, st, il) + * Possible state changes are: + * INITIAL -> decl (when a FUNCTION, FUNCTION_BLOCK, or PROGRAM is found, + * and followed by a VAR declaration) + * INITIAL -> body (when a FUNCTION, FUNCTION_BLOCK, or PROGRAM is found, + * and _not_ followed by a VAR declaration) + * INITIAL -> config (when a CONFIGURATION is found) + * decl -> body (when the last END_VAR is found, i.e. the function body starts) + * body -> st (when it figures out it is parsing st language) + * body -> il (when it figures out it is parsing il language) + * decl -> INITIAL (when a END_FUNCTION, END_FUNCTION_BLOCK, or END_PROGRAM is found) + * st -> INITIAL (when a END_FUNCTION, END_FUNCTION_BLOCK, or END_PROGRAM is found) + * il -> INITIAL (when a END_FUNCTION, END_FUNCTION_BLOCK, or END_PROGRAM is found) + * config -> INITIAL (when a END_CONFIGURATION is found) + */ +/* we are parsing a configuration. */ +%s config + +/* we are parsing a function, program or function block declaration */ +%s decl + +/* we will be parsing a function body. Whether il/st is remains unknown */ +%x body + +/* we are parsing il code -> flex must return the EOL tokens! */ +%s il + +/* we are parsing st code -> flex must not return the EOL tokens! */ +%s st + + + + +/*******************/ +/* File #include's */ +/*******************/ + +/* We extend the IEC 61131-3 standard syntax to allow inclusion + * of other files, using the IEC 61131-3 pragma directive... + * The accepted syntax is: + * {#include ""} + */ + +/* the "include" states are used for picking up the name of an include file */ +%x include_beg +%x include_filename +%x include_end + + +file_include_pragma_filename [^\"]* +file_include_pragma_beg "{#include"{st_whitespace_only}\" +file_include_pragma_end \"{st_whitespace_only}"}" +file_include_pragma {file_include_pragma_beg}{file_include_pragma_filename}{file_include_pragma_end} + + +%{ +#define MAX_INCLUDE_DEPTH 16 + +typedef struct { + YY_BUFFER_STATE buffer_state; + int lineno; + const char *filename; + } include_stack_t; + +include_stack_t include_stack[MAX_INCLUDE_DEPTH]; +int include_stack_ptr = 0; + +const char *INCLUDE_DIRECTORIES[] = { + "", + "lib/", + "/lib/", + "/usr/lib/", + "/usr/lib/iec/", + LIBDIRECTORY "/", + NULL /* must end with NULL!! */ + }; + + +/* + * Join two strings together. Allocate space with malloc(3). + */ +static char *strdup2(const char *a, const char *b) { + char *res = (char *)malloc(strlen(a) + strlen(b) + 1); + + if (!res) + return NULL; + return strcat(strcpy(res, a), b); /* safe, actually */ +} +%} + + + +/*****************************/ +/* Prelimenary constructs... */ +/*****************************/ + + +/* A pragma... */ + +pragma "{"[^}]*"}" + +/* NOTE: this seemingly unnecessary complex definition is required + * to be able to eat up comments such as: + * '(* Testing... ! ***** ******)' + * without using the trailing context command in flex (/{context}) + * since {comment} itself will later be used with + * trailing context ({comment}/{context}) + */ +not_asterisk [^*] +not_close_parenthesis_nor_asterisk [^*)] +asterisk "*" +comment_text {not_asterisk}|(({asterisk}+){not_close_parenthesis_nor_asterisk}) + +comment "(*"({comment_text}*)({asterisk}+)")" + + +/* +3.1 Whitespace + (NOTE: Whitespace IS clearly defined, to include newline!!! See section 2.1.4!!!) + No definition of whitespace is given, in other words, the characters that may be used to seperate language tokens are not pecisely defined. One may nevertheless make an inteligent guess of using the space (' '), and other characters also commonly considered whitespace in other programming languages (horizontal tab, vertical tab, form feed, etc.). + The main question is whether the newline character should be considered whitespace. IL language statements use an EOL token (End Of Line) to distinguish between some language constructs. The EOL token itself is openly defined as "normally consist[ing] of the 'paragraph separator' ", leaving the final choice open to each implemention. If we choose the newline character to represent the EOL token, it may then not be considered whitespace. + On the other hand, some examples that come in a non-normative annex of the specification allow function declarations to span multiple3.1 Whitespace + (NOTE: Whitespace IS clearly defined, to include newline!!! See section 2.1.4!!!) + No definition of whitespace is given, in other words, the characters that may be used to seperate language tokens are not pecisely defined. One may nevertheless make an inteligent guess of using the space (' '), and other characters also commonly considered whitespace in other programming languages (horizontal tab, vertical tab, form feed, etc.). + The main question is whether the newline character should be considered whitespace. IL language statements use an EOL token (End Of Line) to distinguish between some language constructs. The EOL token itself is openly defined as "normally consist[ing] of the 'paragraph separator' ", leaving the final choice open to each implemention. If we choose the newline character to represent the EOL token, it may then not be considered whitespace. + On the other hand, some examples that come in a non-normative annex of the specification allow function declarations to span multiple lines, which means that the newline character is being considered as whitespace. + Our implementation works around this issue by including the new line character in the whitespace while parsing function declarations and the ST language, and parsing it as the EOL token only while parsing IL language statements. This requires the use of a state machine in the lexical parser that needs at least some knowledge of the syntax itself. +*/ +/* NOTE: Our definition of whitespace will only work in ASCII! + * + * Since the IL language needs to know the location of newline + * (token EOL -> '\n' ), we need one definition of whitespace + * for each language... + */ +/* + * NOTE: we cannot use + * st_whitespace [:space:]* + * since we use {st_whitespace} as trailing context. In our case + * this would not constitute "dangerous trailing context", but the + * lexical generator (i.e. flex) does not know this (since it does + * not know which characters belong to the set [:space:]), and will + * generate a "dangerous trailing context" warning! + * We use this alternative just to stop the flex utility from + * generating the invalid (in this case) warning... + */ + +st_whitespace_only [ \f\n\r\t\v]* +il_whitespace_only [ \f\r\t\v]* + +st_whitespace_text {st_whitespace_only}|{comment}|{pragma} +il_whitespace_text {il_whitespace_only}|{comment}|{pragma} + +st_whitespace {st_whitespace_text}* +il_whitespace {il_whitespace_text}* + +st_whitespace_text_no_pragma {st_whitespace_only}|{comment} +il_whitespace_text_no_pragma {il_whitespace_only}|{comment} + +st_whitespace_no_pragma {st_whitespace_text_no_pragma}* +il_whitespace_no_pragma {il_whitespace_text_no_pragma}* + +qualified_identifier {identifier}(\.{identifier})? + + + +/*****************************************/ +/* B.1.1 Letters, digits and identifiers */ +/*****************************************/ +/* NOTE: The following definitions only work if the host computer + * is using the ASCII maping. For e.g., with EBCDIC [A-Z] + * contains non-alphabetic characters! + * The correct way of doing it would be to use + * the [:upper:] etc... definitions. + * + * Unfortunately, further on we need all printable + * characters (i.e. [:print:]), but excluding '$'. + * Flex does not allow sets to be composed by excluding + * elements. Sets may only be constructed by adding new + * elements, which means that we have to revert to + * [\x20\x21\x23\x25\x26\x28-x7E] for the definition + * of the printable characters with the required exceptions. + * The above also implies the use of ASCII, but now we have + * no way to work around it| + * + * The conclusion is that our parser is limited to ASCII + * based host computers!! + */ +letter [A-Za-z] +digit [0-9] +octal_digit [0-7] +hex_digit {digit}|[A-F] +identifier ({letter}|(_({letter}|{digit})))((_?({letter}|{digit}))*) + + +/*******************/ +/* B.1.2 Constants */ +/*******************/ + +/******************************/ +/* B.1.2.1 Numeric literals */ +/******************************/ +integer {digit}((_?{digit})*) +binary_integer 2#{bit}((_?{bit})*) +bit [0-1] +octal_integer 8#{octal_digit}((_?{octal_digit})*) +hex_integer 16#{hex_digit}((_?{hex_digit})*) +exponent [Ee]([+-]?){integer} +/* The correct definition for real would be: + * real {integer}\.{integer}({exponent}?) + * + * Unfortunately, the spec also defines fixed_point (B 1.2.3.1) as: + * fixed_point {integer}\.{integer} + * + * This means that {integer}\.{integer} could be interpreted + * as either a fixed_point or a real. + * I have opted to interpret {integer}\.{integer} as a fixed_point. + * In order to do this, the definition of real has been changed to: + * real {integer}\.{integer}{exponent} + * + * This means that the syntax parser now needs to define a real to be + * either a real_token or a fixed_point_token! + */ +real {integer}\.{integer}{exponent} + + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ +/* +common_character_representation := + +|'$$' +|'$L'|'$N'|'$P'|'$R'|'$T' +|'$l'|'$n'|'$p'|'$r'|'$t' + +NOTE: $ = 0x24 + " = 0x22 + ' = 0x27 + + printable chars in ASCII: 0x20-0x7E +*/ + +esc_char_u $L|$N|$P|$R|$T +esc_char_l $l|$n|$p|$r|$t +esc_char $$|{esc_char_u}|{esc_char_l} +double_byte_char (${hex_digit}{hex_digit}{hex_digit}{hex_digit}) +single_byte_char (${hex_digit}{hex_digit}) + +/* WARNING: + * This definition is only valid in ASCII... + * + * Flex includes the function print_char() that defines + * all printable characters portably (i.e. whatever character + * encoding is currently being used , ASCII, EBCDIC, etc...) + * Unfortunately, we cannot generate the definition of + * common_character_representation portably, since flex + * does not allow definition of sets by subtracting + * elements in one set from another set. + * This means we must build up the defintion of + * common_character_representation using only set addition, + * which leaves us with the only choice of defining the + * characters non-portably... + */ +common_character_representation [\x20\x21\x23\x25\x26\x28-\x7E]|{esc_char} +double_byte_character_representation $\"|'|{double_byte_char}|{common_character_representation} +single_byte_character_representation $'|\"|{single_byte_char}|{common_character_representation} + + +double_byte_character_string \"({double_byte_character_representation}*)\" +single_byte_character_string '({single_byte_character_representation}*)' + + +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +fixed_point {integer}\.{integer} + +fixed_point_d {fixed_point}d +integer_d {integer}d + +fixed_point_h {fixed_point}h +integer_h {integer}h + +fixed_point_m {fixed_point}m +integer_m {integer}m + +fixed_point_s {fixed_point}s +integer_s {integer}s + +fixed_point_ms {fixed_point}ms +integer_ms {integer}ms + + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +/* The correct definition, if the standard were to be followed... */ +/* +location_prefix [IQM] +size_prefix [XBWDL] +direct_variable %{location_prefix}({size_prefix}?){integer}((.{integer})*) +*/ + +/* For the MatPLC, we will accept % + * as a direct variable, this being mapped onto the MatPLC point + * named + */ +/* TODO: we should not restrict it to only the accepted syntax + * of as specified by the standard. MatPLC point names + * have a more permissive syntax. + * + * e.g. "P__234" + * Is a valid MatPLC point name, but not a valid !! + * The same happens with names such as "333", "349+23", etc... + * How can we handle these more expressive names in our case? + * Remember that some direct variable may remain anonymous, with + * declarations such as: + * VAR + * AT %I3 : BYTE := 255; + * END_VAR + * in which case we are currently using "I3" as the variable + * name. For the other names, this would create havoc!!! + */ +direct_variable %{identifier} + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ +incompl_location %[IQM]\* + + + + +%% + /* fprintf(stderr, "flex: state %d\n", YY_START); */ + + /*****************************************************/ + /*****************************************************/ + /*****************************************************/ + /***** *****/ + /***** *****/ + /***** F I R S T T H I N G S F I R S T *****/ + /***** *****/ + /***** *****/ + /*****************************************************/ + /*****************************************************/ + /*****************************************************/ + + /*********************************/ + /* Handle the pragmas! */ + /*********************************/ + + /* We start off by searching for the pragmas we handle in the lexical parser. */ +{file_include_pragma} unput_text(0); yy_push_state(include_beg); + + /* Any other pragma we find, we just pass it up to the syntax parser... */ + /* Note that the state is exclusive, so we have to include it here too. */ +{pragma} {/* return the pragmma without the enclosing '{' and '}' */ + yytext[strlen(yytext)-2] = '\0'; + yylval.ID=strdup(yytext+1); + return pragma_token; + } +{pragma} {/* return the pragmma without the enclosing '{' and '}' */ + yytext[strlen(yytext)-2] = '\0'; + yylval.ID=strdup(yytext+1); + return pragma_token; + } + + + /*********************************/ + /* Handle the file includes! */ + /*********************************/ +{file_include_pragma_beg} BEGIN(include_filename); + +{file_include_pragma_filename} { + /* got the include file name */ + int i; + + if (include_stack_ptr >= MAX_INCLUDE_DEPTH) { + fprintf(stderr, "Includes nested too deeply\n"); + exit( 1 ); + } + + (include_stack[include_stack_ptr]).buffer_state = YY_CURRENT_BUFFER; + (include_stack[include_stack_ptr]).lineno = yylineno; + (include_stack[include_stack_ptr]).filename = current_filename; + include_stack_ptr++; + yylineno = 1; + current_filename = strdup(yytext); + + for (i = 0, yyin = NULL; (INCLUDE_DIRECTORIES[i] != NULL) && (yyin == NULL); i++) { + char *full_name = strdup2(INCLUDE_DIRECTORIES[i], yytext); + if (full_name == NULL) { + fprintf(stderr, "Out of memory!\n"); + exit( 1 ); + } + yyin = fopen(full_name, "r"); + free(full_name); + } + + if (!yyin) { + fprintf(stderr, "Error opening included file %s\n", yytext); + exit( 1 ); + } + + /* switch input buffer to new file... */ + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); + /* switch to whatever state was active before the include file */ + yy_pop_state(); + /* now process the new file... */ + } + + +<> { + if (--include_stack_ptr < 0) { + yyterminate(); + } else { + yy_delete_buffer(YY_CURRENT_BUFFER); + yy_switch_to_buffer((include_stack[include_stack_ptr]).buffer_state); + yylineno = include_stack[include_stack_ptr].lineno; + /* removing constness of char *. This is safe actually, + * since the only real const char * that is stored on the stack is + * the first one (i.e. the opne that gets stored in include_stack[0], + * which is never free'd! + */ + free((char *)current_filename); + current_filename = include_stack[include_stack_ptr].filename; + yy_push_state(include_end); + } + } + +{file_include_pragma_end} yy_pop_state(); + + + /*********************************/ + /* Handle all the state changes! */ + /*********************************/ + + /* INITIAL -> decl */ +{ + /* NOTE: how about functions that do not delcare variables, and go directly to the body??? + * - According to Section 2.5.1.3 (Function Declaration), item 2 in the list, a FUNCTION + * must have at least one input argument, so a correct declaration will have at least + * one VAR_INPUT ... VAR_END construct! + * - According to Section 2.5.2.2 (Function Block Declaration), a FUNCTION_BLOCK + * must have at least one input argument, so a correct declaration will have at least + * one VAR_INPUT ... VAR_END construct! + * - According to Section 2.5.3 (Programs), a PROGRAM must have at least one input + * argument, so a correct declaration will have at least one VAR_INPUT ... VAR_END + * construct! + * + * All the above means that we needn't worry about PROGRAMs, FUNCTIONs or + * FUNCTION_BLOCKs that do not have at least one VAR_END before the body. + * If the code has an error, and no VAR_END before the body, we will simply + * continue in the state, untill the end of the FUNCTION, FUNCTION_BLOCK + * or PROGAM. + */ +FUNCTION BEGIN(decl); return FUNCTION; +FUNCTION_BLOCK BEGIN(decl); return FUNCTION_BLOCK; +PROGRAM BEGIN(decl); return PROGRAM; +CONFIGURATION BEGIN(config); return CONFIGURATION; +} + + /* INITIAL -> body */ + /* required if the function, program, etc.. has no VAR block! */ +{ +FUNCTION BEGIN(body); return FUNCTION; +FUNCTION_BLOCK BEGIN(body); return FUNCTION_BLOCK; +PROGRAM BEGIN(body); return PROGRAM; +} + + /* decl -> body */ +{ +END_VAR{st_whitespace}VAR unput_text(strlen("END_VAR")); return END_VAR; +END_VAR{st_whitespace} unput_text(strlen("END_VAR")); BEGIN(body); return END_VAR; +} + + /* body -> (il | st) */ +{ +{qualified_identifier}{st_whitespace}":=" unput_text(0); BEGIN(st); +{qualified_identifier}"[" unput_text(0); BEGIN(st); + +RETURN unput_text(0); BEGIN(st); +IF unput_text(0); BEGIN(st); +CASE unput_text(0); BEGIN(st); +FOR unput_text(0); BEGIN(st); +WHILE unput_text(0); BEGIN(st); +REPEAT unput_text(0); BEGIN(st); +EXIT unput_text(0); BEGIN(st); + + +{identifier} {int token = get_identifier_token(yytext); + if (token == prev_declared_fb_name_token) { + /* the code has a call to a function block */ + BEGIN(st); + } else { + BEGIN(il); + } + unput_text(0); + } +. unput_text(0); BEGIN(il); + +} /* end of body lexical parser */ + + /* (decl | body | il | st) -> INITIAL */ +END_FUNCTION BEGIN(INITIAL); return END_FUNCTION; +END_FUNCTION_BLOCK BEGIN(INITIAL); return END_FUNCTION_BLOCK; +END_PROGRAM BEGIN(INITIAL); return END_PROGRAM; + + /* config -> INITIAL */ +END_CONFIGURATION BEGIN(INITIAL); return END_CONFIGURATION; + + + + /***************************************/ + /* Next is to to remove all whitespace */ + /***************************************/ + /* NOTE: pragmas are handled right at the beginning... */ + +{st_whitespace_no_pragma} /* Eat any whitespace */ +{il_whitespace_no_pragma} /* Eat any whitespace */ + + + /*****************************************/ + /* B.1.1 Letters, digits and identifiers */ + /*****************************************/ + /* NOTE: 'R1', 'IN', etc... are IL operators, and therefore tokens + * On the other hand, the spec does not define them as keywords, + * which means they may be re-used for variable names, etc...! + * The syntax parser already caters for the possibility of these + * tokens being used for variable names in their declarations. + * When they are declared, they will be added to the variable symbol table! + * Further appearances of these tokens must no longer be parsed + * as R1_tokens etc..., but rather as variable_name_tokens! + * + * That is why the first thing we do with identifiers, even before + * checking whether they may be a 'keyword', is to check whether + * they have been previously declared as a variable name, + * + * TODO: how about function names? + */ +{identifier} {int token = get_identifier_token(yytext); + if ((token == prev_declared_variable_name_token) || + (token == prev_declared_fb_name_token)) { + /* + if (token != identifier_token) + */ + /* NOTE: if we use the above line, then 'MOD' et al must be removed + * from the library_symbol_table as a default function name! + */ + yylval.ID=strdup(yytext); + return token; + } + /* otherwise, leave it for the other lexical parser rules... */ + REJECT; + } + + + /******************************************************/ + /******************************************************/ + /******************************************************/ + /***** *****/ + /***** *****/ + /***** N O W D O T H E K E Y W O R D S *****/ + /***** *****/ + /***** *****/ + /******************************************************/ + /******************************************************/ + /******************************************************/ + + +EN return EN; +ENO return ENO; + + + /******************************/ + /* B 1.2.1 - Numeric Literals */ + /******************************/ +TRUE return TRUE; +BOOL#1 return TRUE; +FALSE return FALSE; +BOOL#0 return FALSE; + + + /************************/ + /* B 1.2.3.1 - Duration */ + /************************/ +t# return T_SHARP; +T# return T_SHARP; +TIME return TIME; + + + /************************************/ + /* B 1.2.3.2 - Time of day and Date */ + /************************************/ +TIME_OF_DAY return TIME_OF_DAY; +TOD return TIME_OF_DAY; +DATE return DATE; +d# return D_SHARP; +D# return D_SHARP; +DATE_AND_TIME return DATE_AND_TIME; +DT return DATE_AND_TIME; + + + /***********************************/ + /* B 1.3.1 - Elementary Data Types */ + /***********************************/ +BYTE return BYTE; +WORD return WORD; +DWORD return DWORD; +LWORD return LWORD; + + + /********************************/ + /* B 1.3.2 - Generic data types */ + /********************************/ + /* Strangely, the following symbols do not seem to be required! */ + /* But we include them so they become reserved words, and do not + * get passed up to bison as an identifier... + */ +ANY return ANY; +ANY_DERIVED return ANY_DERIVED; +ANY_ELEMENTARY return ANY_ELEMENTARY; +ANY_MAGNITUDE return ANY_MAGNITUDE; +ANY_NUM return ANY_NUM; +ANY_REAL return ANY_REAL; +ANY_INT return ANY_INT; +ANY_BIT return ANY_BIT; +ANY_STRING return ANY_STRING; +ANY_DATE return ANY_DATE; + + + /********************************/ + /* B 1.3.3 - Derived data types */ + /********************************/ +":=" return ASSIGN; +".." return DOTDOT; +TYPE return TYPE; +END_TYPE return END_TYPE; +ARRAY return ARRAY; +OF return OF; +STRUCT return STRUCT; +END_STRUCT return END_STRUCT; + + + /*********************/ + /* B 1.4 - Variables */ + /*********************/ +REAL return REAL; +LREAL return LREAL; + +SINT return SINT; +INT return INT; +DINT return DINT; +LINT return LINT; + +USINT return USINT; +UINT return UINT; +UDINT return UDINT; +ULINT return ULINT; + + +WSTRING return WSTRING; +STRING return STRING; +BOOL return BOOL; + +TIME return TIME; +DATE return DATE; +DT return DT; +TOD return TOD; +DATE_AND_TIME return DATE_AND_TIME; +TIME_OF_DAY return TIME_OF_DAY; + + + /******************************************/ + /* B 1.4.3 - Declaration & Initialisation */ + /******************************************/ +VAR_INPUT return VAR_INPUT; +VAR_OUTPUT return VAR_OUTPUT; +VAR_IN_OUT return VAR_IN_OUT; +VAR_EXTERNAL return VAR_EXTERNAL; +VAR_GLOBAL return VAR_GLOBAL; +END_VAR return END_VAR; +RETAIN return RETAIN; +NON_RETAIN return NON_RETAIN; +R_EDGE return R_EDGE; +F_EDGE return F_EDGE; +AT return AT; + + + /***********************/ + /* B 1.5.1 - Functions */ + /***********************/ +FUNCTION return FUNCTION; +END_FUNCTION return END_FUNCTION; +VAR return VAR; +CONSTANT return CONSTANT; + + + /*****************************/ + /* B 1.5.2 - Function Blocks */ + /*****************************/ +FUNCTION_BLOCK return FUNCTION_BLOCK; +END_FUNCTION_BLOCK return END_FUNCTION_BLOCK; +VAR_TEMP return VAR_TEMP; +VAR return VAR; +NON_RETAIN return NON_RETAIN; +END_VAR return END_VAR; + + + /**********************/ + /* B 1.5.3 - Programs */ + /**********************/ +PROGRAM return PROGRAM; +END_PROGRAM return END_PROGRAM; + + + /********************************************/ + /* B 1.6 Sequential Function Chart elements */ + /********************************************/ + /* NOTE: the following identifiers/tokens clash with the R and S IL operators, as well + .* as other identifiers that may be used as variable names inside IL and ST programs. + * They will have to be handled when we include parsing of SFC... For now, simply + * ignore them! + */ + /* +ACTION return ACTION; +END_ACTION return END_ACTION; + +TRANSITION return TRANSITION; +END_TRANSITION return END_TRANSITION; +FROM return FROM; +TO return TO; +PRIORITY return PRIORITY; + +INITIAL_STEP return INITIAL_STEP; +STEP return STEP; +END_STEP return END_STEP; + +L return L; +D return D; +SD return SD; +DS return DS; +SL return SL; + +N return N; +P return P; + +R return R; +S return S; + */ + + + /********************************/ + /* B 1.7 Configuration elements */ + /********************************/ +CONFIGURATION return CONFIGURATION; +END_CONFIGURATION return END_CONFIGURATION; +TASK return TASK; +RESOURCE return RESOURCE; +ON return ON; +END_RESOURCE return END_RESOURCE; +VAR_CONFIG return VAR_CONFIG; +VAR_ACCESS return VAR_ACCESS; +END_VAR return END_VAR; +WITH return WITH; +PROGRAM return PROGRAM; +RETAIN return RETAIN; +NON_RETAIN return NON_RETAIN; +PRIORITY return PRIORITY; +SINGLE return SINGLE; +INTERVAL return INTERVAL; +READ_WRITE return READ_WRITE; +READ_ONLY return READ_ONLY; + + + /***********************************/ + /* B 2.1 Instructions and Operands */ + /***********************************/ +\n return EOL; + + + /*******************/ + /* B 2.2 Operators */ + /*******************/ + /* NOTE: we can't have flex return the same token for + * ANDN and &N, neither for AND and &, since + * AND and ANDN are considered valid variable + * function or functionblock type names! + * This means that the parser may decide that the + * AND or ANDN strings found in the source code + * are being used as variable names + * and not as operators, and will therefore transform + * these tokens into indentifier tokens! + * We can't have the parser thinking that the source + * code contained the string AND (which may be interpreted + * as a vairable name) when in reality the source code + * merely contained the character &, so we use two + * different tokens for & and AND (and similarly + * ANDN and &N)! + */ +LD return LD; +LDN return LDN; +ST return ST; +STN return STN; +NOT return NOT; +S return S; +R return R; +S1 return S1; +R1 return R1; +CLK return CLK; +CU return CU; +CD return CD; +PV return PV; +IN return IN; +PT return PT; +AND return AND; +& return AND2; +OR return OR; +XOR return XOR; +ANDN return ANDN; +&N return ANDN2; +ORN return ORN; +XORN return XORN; +ADD return ADD; +SUB return SUB; +MUL return MUL; +DIV return DIV; +MOD return MOD; +GT return GT; +GE return GE; +EQ return EQ; +LT return LT; +LE return LE; +NE return NE; +CAL return CAL; +CALC return CALC; +CALCN return CALCN; +RET return RET; +RETC return RETC; +RETCN return RETCN; +JMP return JMP; +JMPC return JMPC; +JMPCN return JMPCN; + + + /***********************/ + /* B 3.1 - Expressions */ + /***********************/ +"**" return OPER_EXP; +"<>" return OPER_NE; +">=" return OPER_GE; +"<=" return OPER_LE; +AND return AND; +XOR return XOR; +OR return OR; +NOT return NOT; +MOD return MOD; + + + /*****************************************/ + /* B 3.2.2 Subprogram Control Statements */ + /*****************************************/ +:= return ASSIGN; +=> return SENDTO; +RETURN return RETURN; + + + /********************************/ + /* B 3.2.3 Selection Statements */ + /********************************/ +IF return IF; +THEN return THEN; +ELSIF return ELSIF; +ELSE return ELSE; +END_IF return END_IF; + +CASE return CASE; +OF return OF; +ELSE return ELSE; +END_CASE return END_CASE; + + + /********************************/ + /* B 3.2.4 Iteration Statements */ + /********************************/ +FOR return FOR; +TO return TO; +BY return BY; +DO return DO; +END_FOR return END_FOR; + +WHILE return WHILE; +DO return DO; +END_WHILE return END_WHILE; + +REPEAT return REPEAT; +UNTIL return UNTIL; +END_REPEAT return END_REPEAT; + +EXIT return EXIT; + + + + + + /********************************************************/ + /********************************************************/ + /********************************************************/ + /***** *****/ + /***** *****/ + /***** N O W W O R K W I T H V A L U E S *****/ + /***** *****/ + /***** *****/ + /********************************************************/ + /********************************************************/ + /********************************************************/ + + + /********************************************/ + /* B.1.4.1 Directly Represented Variables */ + /********************************************/ +{direct_variable} {yylval.ID=strdup(yytext); return direct_variable_token;} + + + /******************************************/ + /* B 1.4.3 - Declaration & Initialisation */ + /******************************************/ +{incompl_location} {yylval.ID=strdup(yytext); return incompl_location_token;} + + + /************************/ + /* B 1.2.3.1 - Duration */ + /************************/ +{fixed_point} {yylval.ID=strdup(yytext); return fixed_point_token;} + +{fixed_point_d} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_d_token;} +{integer_d} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_d_token;} + +{fixed_point_h} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_h_token;} +{integer_h} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_h_token;} + +{fixed_point_m} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_m_token;} +{integer_m} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_m_token;} + +{fixed_point_s} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return fixed_point_s_token;} +{integer_s} {yylval.ID=strdup(yytext); yylval.ID[yyleng-1] = '\0'; return integer_s_token;} + +{fixed_point_ms} {yylval.ID=strdup(yytext); yylval.ID[yyleng-2] = '\0'; return fixed_point_ms_token;} +{integer_ms} {yylval.ID=strdup(yytext); yylval.ID[yyleng-2] = '\0'; return integer_ms_token;} + + + /*******************************/ + /* B.1.2.2 Character Strings */ + /*******************************/ +{double_byte_character_string} {yylval.ID=strdup(yytext); return double_byte_character_string_token;} +{single_byte_character_string} {yylval.ID=strdup(yytext); return single_byte_character_string_token;} + + + /******************************/ + /* B.1.2.1 Numeric literals */ + /******************************/ +{integer} {yylval.ID=strdup(yytext); return integer_token;} +{real} {yylval.ID=strdup(yytext); return real_token;} +{binary_integer} {yylval.ID=strdup(yytext); return binary_integer_token;} +{octal_integer} {yylval.ID=strdup(yytext); return octal_integer_token;} +{hex_integer} {yylval.ID=strdup(yytext); return hex_integer_token;} + + + /*****************************************/ + /* B.1.1 Letters, digits and identifiers */ + /*****************************************/ +{identifier}/({st_whitespace})"=>" {yylval.ID=strdup(yytext); return sendto_identifier_token;} +{identifier}/({il_whitespace})"=>" {yylval.ID=strdup(yytext); return sendto_identifier_token;} +{identifier} {yylval.ID=strdup(yytext); + /*printf("returning identifier...: %s, %d\n", yytext, get_identifier_token(yytext));*/ + return get_identifier_token(yytext);} + + + + + + + /************************************************/ + /************************************************/ + /************************************************/ + /***** *****/ + /***** *****/ + /***** T H E L E F T O V E R S . . . *****/ + /***** *****/ + /***** *****/ + /************************************************/ + /************************************************/ + /************************************************/ + + /* do the single character tokens... + * + * e.g.: ':' '(' ')' '+' '*' ... + */ +. {return yytext[0];} + + +%% + + + + + +/***********************************/ +/* Utility function definitions... */ +/***********************************/ + +/* print the include file stack to stderr... */ +void print_include_stack(void) { + int i; + + if ((include_stack_ptr - 1) >= 0) + fprintf (stderr, "in file "); + for (i = include_stack_ptr - 1; i >= 0; i--) + fprintf (stderr, "included from file %s:%d\n", include_stack[i].filename, include_stack[i].lineno); +} + + +/* return all the text in the current token back to the input stream, except the first n chars. */ +void unput_text(unsigned int n) { + /* it seems that flex has a bug in that it will not correctly count the line numbers + * if we return newlines back to the input stream. These newlines will be re-counted + * a second time when they are processed again by flex. + * We therefore determine how many newlines are in the text we are returning, + * and decrement the line counter acordingly... + */ + unsigned int i; + + for (i = n; i < strlen(yytext); i++) + if (yytext[i] == '\n') + yylineno--; + + /* now return all the text back to the input stream... */ + yyless(n); +} + + +/* Called by flex when it reaches the end-of-file */ +int yywrap(void) +{ + /* We reached the end of the input file... */ + + /* Should we continue with another file? */ + /* If so: + * open the new file... + * return 0; + */ + + /* to we stop processing... + * + * return 1; + */ + + + return 1; /* Stop scanning at end of input file. */ +} + + + +/*************************************/ +/* Include a main() function to test */ +/* the token parsing by flex.... */ +/*************************************/ +#ifdef TEST_MAIN + +#include "../util/symtable.hh" + +yystype yylval; +YYLTYPE yylloc; + +const char *current_filename; + +int get_identifier_token(const char *identifier_str) {return 0;} + + + + + +int main(int argc, char **argv) { + + FILE *in_file; + int res; + + if (argc == 1) { + /* Work as an interactive (command line) parser... */ + while((res=yylex())) + fprintf(stderr, "(line %d)token: %d\n", yylineno, res); + } else { + /* Work as non-interactive (file) parser... */ + if((in_file = fopen(argv[1], "r")) == NULL) { + char *errmsg = strdup2("Error opening main file ", argv[1]); + perror(errmsg); + free(errmsg); + return -1; + } + + /* parse the file... */ + yyin = in_file; + current_filename = argv[1]; + while(1) { + res=yylex(); + fprintf(stderr, "(line %d)token: %d (%s)\n", yylineno, res, yylval.ID); + } + } + + return 0; + +} +#endif diff -r 000000000000 -r fb772792efd1 stage1_2/iec.y --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage1_2/iec.y Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,5422 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* + * Stage 2 + * ======= + * + * This file contains the syntax definition of the textual + * languages IL and ST. The syntax parser, comprising the + * 2nd stage of the overall compiler, is generated by runing + * bison on this file. + */ + + + + +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/******* *******/ +/******* The following syntax does not have any conflicts. *******/ +/******* *******/ +/******* P L E A S E K E E P I T T H A T W A Y ! *******/ +/******* =================================================== *******/ +/******* *******/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ +/**********************************************************************/ + + + + +%{ +#include /* required for strdup() */ + + +/* declare the token parser generated by flex... */ +int yylex(void); + +/* declare the error handler defined at the end of this file */ +void yyerror (const char *error_msg); + +/* produce a more verbose parsing error message */ +#define YYERROR_VERBOSE + +/* Include debuging code. + * Printing of debug info must then be activated by setting + * the variable yydebug to 1. + */ +#define YYDEBUG 0 + + +/* file with declaration of absyntax classes... */ +#include "../absyntax/absyntax.hh" + +/* file with declaration of token constants. Generated by bison! */ +#include "iec.y.hh" + +/* file with the declarations of symbol tables... */ +#include "../util/symtable.hh" + + +/* an ugly hack!! + * We will probably not need it when we decide + * to cut down the abstract syntax down to size. + * We keep it as it is until we get to write + * stages 3 and 4 of the compiler. Who knows, + * we might just find out that we really do need + * the abstract syntax tree to stay as it is + * afterall! + */ +/* for each element in list_c * + * execute the code + */ +#define FOR_EACH_ELEMENT(elem, list, code) { \ + symbol_c *elem; \ + for(int i = 0; i < list->n; i++) { \ + elem = list->elements[i]; \ + code; \ + } \ +} + + +/* A macro for printing out internal parser errors... */ +#define ERROR error_exit(__FILE__,__LINE__) +/* function defined in main.cc */ +extern void error_exit(const char *file_name, int line_no); + + +/*********************************/ +/* The global symbol tables... */ +/*********************************/ +/* NOTE: declared static because they are not accessed + * directly by the lexical parser (flex), but rather + * through the function get_identifier_token() + */ +/* A symbol table to store all the library elements */ +/* e.g.: + * + * + * + * + */ +static symtable_c library_element_symtable; + +/* A symbol table to store the declared variables of + * the function currently being parsed... + */ +static symtable_c variable_name_symtable; + + +/*************************/ +/* global variables... */ +/*************************/ +static symbol_c *tree_root = NULL; + +/* The name of the file currently being parsed... + * Note that flex accesses and updates this global variable + * apropriately whenever it comes across an (*#include *) + * directive... + */ +const char *current_filename = NULL; + +/* A global flag used to tell the parser if overloaded funtions should be allowed. + * The IEC 61131-3 standard allows overloaded funtions in the standard library, + * but disallows them in user code... + */ +bool allow_function_overloading = false; + + +/************************/ +/* forward declarations */ +/************************/ +/* The functions declared here are defined at the end of this file... */ + +/* Convert an il_operator_c into an identifier_c */ +symbol_c *il_operator_c_2_identifier_c(symbol_c *il_operator); + +/* print an error message */ +void print_err_msg(const char *filename, int lineno, const char *additional_error_msg); + + +/************************/ +/* forward declarations */ +/************************/ +/* The functions declared here are defined in iec.flex... */ +void print_include_stack(void); + + +%} + + + +%union { + symbol_c *leaf; + list_c *list; + char *ID; /* token value */ + struct { + symbol_c *first; + symbol_c *second; + } double_symbol; /* used by il_simple_operator_clash_il_operand */ +} + + + + + + +/*****************************/ +/* Prelimenary constructs... */ +/*****************************/ +%type start + +%type any_identifier + +%token prev_declared_variable_name_token +%token prev_declared_fb_name_token +%type prev_declared_variable_name +%type prev_declared_fb_name + +%token prev_declared_simple_type_name_token +%token prev_declared_subrange_type_name_token +%token prev_declared_enumerated_type_name_token +%token prev_declared_array_type_name_token +%token prev_declared_structure_type_name_token +%token prev_declared_string_type_name_token + +%type prev_declared_simple_type_name +%type prev_declared_subrange_type_name +%type prev_declared_enumerated_type_name +%type prev_declared_array_type_name +%type prev_declared_structure_type_name +%type prev_declared_string_type_name + +%token prev_declared_derived_function_name_token +%token prev_declared_derived_function_block_name_token +%token prev_declared_program_type_name_token +%type prev_declared_derived_function_name +%type prev_declared_derived_function_block_name +%type prev_declared_program_type_name + + +/* A bogus token that, in principle, flex MUST NEVER generate */ +/* USE 1: + * ====== + * This token is currently also being used as the default + * initialisation value of the token_id member in + * the symbol_c base class. + * + * USE 2 + * ===== + * This token may also be used in the future to remove + * mysterious reduce/reduce conflicts due to the fact + * that our grammar may not be LALR(1) but merely LR(1). + * This means that bison cannot handle it without some + * caoxing from ourselves. We will then need this token + * to do the coaxing... + */ +%token BOGUS_TOKEN_ID + + +/* The pragmas... */ +%token pragma_token +%type pragma + + +/* Where do these tokens belong ?? */ +/* TODO: get the syntax parser to handle these tokens... */ +%token EN +%token ENO + + + +/***************************/ +/* B 0 - Programming Model */ +/***************************/ +%type library +%type library_element_declaration + + +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +/* Done totally within flex... + letter + digit + octal_digit + hex_digit +*/ +%token identifier_token +%type identifier + + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ +%type constant +/* a helper symbol for expression */ +%type non_negative_constant + + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ +/* Done totally within flex... + bit +*/ +%type numeric_literal +/* helper symbol for non_negative_constant */ +%type non_negative_numeric_literal +%type integer_literal +%type signed_integer +/* a helper symbol for non_negative_constant */ +%type non_negative_signed_integer +%token integer_token +%type integer +%token binary_integer_token +%type binary_integer +%token octal_integer_token +%type octal_integer +%token hex_integer_token +%type hex_integer +%token real_token +%type real +%type signed_real +/* helper symbol for non_negative_real_literal */ +%type non_negative_signed_real +%type real_literal +/* helper symbol for non_negative_numeric_literal */ +%type non_negative_real_literal +// %type exponent +%type bit_string_literal +%type boolean_literal + +%token FALSE +%token TRUE + + +/*******************************/ +/* B 1.2.2 - Character Strings */ +/*******************************/ +%token single_byte_character_string_token +%token double_byte_character_string_token + +%type character_string +%type single_byte_character_string +%type double_byte_character_string + + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +%type time_literal + + +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +%type duration +%type interval +%type days +%type fixed_point +%type hours +%type minutes +%type seconds +%type milliseconds + +%type integer_d +%type integer_h +%type integer_m +%type integer_s +%type integer_ms +%type fixed_point_d +%type fixed_point_h +%type fixed_point_m +%type fixed_point_s +%type fixed_point_ms + +%token fixed_point_token +%token fixed_point_d_token +%token integer_d_token +%token fixed_point_h_token +%token integer_h_token +%token fixed_point_m_token +%token integer_m_token +%token fixed_point_s_token +%token integer_s_token +%token fixed_point_ms_token +%token integer_ms_token + +%token TIME +%token T_SHARP + + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ +%type time_of_day +%type daytime +%type day_hour +%type day_minute +%type day_second +%type date +%type date_literal +%type year +%type month +%type day +%type date_and_time + +%token TIME_OF_DAY +%token DATE +%token D_SHARP +%token DATE_AND_TIME + + +/**********************/ +/* B 1.3 - Data Types */ +/**********************/ +/* Strangely, the following symbol does seem to be required! */ +// %type data_type_name +%type non_generic_type_name + + +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ +/* NOTES: + * + * - To make the definition of bit_string_literal more + * concise, it is useful to use an extra non-terminal + * symbol (i.e. a grouping or construct) that groups the + * following elements (BYTE, WORD, DWORD, LWORD). + * Note that the definition of bit_string_type_name + * (according to the spec) includes the above elements + * and an extra BOOL. + * We could use an extra construct with the first four + * elements to be used solely in the definition of + * bit_string_literal, but with the objective of not + * having to replicate the actions (if we ever need + * to change them, they would need to be changed in both + * bit_string_type_name and the extra grouping), we + * have re-defined bit_string_type_name as only including + * the first four elements. + * In order to have our parser implement the specification + * correctly we have augmented every occurence of + * bit_string_type_name in other rules with the BOOL + * token. Since bit_string_type_name only appears in + * the rule for elementary_type_name, this does not + * seem to be a big concession to make! + * + * - We have added a helper symbol to concentrate the + * instantiation of STRING and WSTRING into a single + * location (elementary_string_type_name). + * These two elements show up in several other rules, + * but we want to create the equivalent abstract syntax + * in a single location of this file, in order to make + * possible future changes easier to edit... + */ +%type elementary_type_name +%type numeric_type_name +%type integer_type_name +%type signed_integer_type_name +%type unsigned_integer_type_name +%type real_type_name +%type date_type_name +%type bit_string_type_name +/* helper symbol to concentrate the instantiation + * of STRING and WSTRING into a single location + */ +%type elementary_string_type_name + +%token BYTE +%token WORD +%token DWORD +%token LWORD + +%token LREAL +%token REAL + +%token SINT +%token INT +%token DINT +%token LINT + +%token USINT +%token UINT +%token UDINT +%token ULINT + +%token WSTRING +%token STRING +%token BOOL + +%token TIME +%token DATE +%token DATE_AND_TIME +%token DT +%token TIME_OF_DAY +%token TOD + + +/********************************/ +/* B 1.3.2 - Generic data types */ +/********************************/ +/* Strangely, the following symbol does seem to be required! */ +// %type generic_type_name + +/* The following tokens do not seem to be used either + * but we declare them so they become reserved words... + */ +%token ANY +%token ANY_DERIVED +%token ANY_ELEMENTARY +%token ANY_MAGNITUDE +%token ANY_NUM +%token ANY_REAL +%token ANY_INT +%token ANY_BIT +%token ANY_STRING +%token ANY_DATE + + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +%type derived_type_name +%type single_element_type_name +// %type simple_type_name +// %type subrange_type_name +// %type enumerated_type_name +// %type array_type_name +// %type structure_type_name + +%type data_type_declaration +/* helper symbol for data_type_declaration */ +%type type_declaration_list +%type type_declaration +%type single_element_type_declaration + +%type simple_type_declaration +%type simple_spec_init +%type simple_specification + +%type subrange_type_declaration +%type subrange_spec_init +%type subrange_specification +%type subrange + +%type enumerated_type_declaration +%type enumerated_spec_init +%type enumerated_specification +/* helper symbol for enumerated_value */ +%type enumerated_value_list +%type enumerated_value + +%type array_type_declaration +%type array_spec_init +%type array_specification +/* helper symbol for array_specification */ +%type array_subrange_list +%type array_initialization +/* helper symbol for array_initialization */ +%type array_initial_elements_list +%type array_initial_elements +%type array_initial_element + +%type structure_type_declaration +%type structure_specification +%type initialized_structure +%type structure_declaration +/* helper symbol for structure_declaration */ +%type structure_element_declaration_list +%type structure_element_declaration +%type structure_element_name +%type structure_initialization +/* helper symbol for structure_initialization */ +%type structure_element_initialization_list +%type structure_element_initialization + +//%type string_type_name +%type string_type_declaration +/* helper symbol for string_type_declaration */ +%type string_type_declaration_size +/* helper symbol for string_type_declaration */ +%type string_type_declaration_init + +%token ASSIGN +%token DOTDOT /* ".." */ +%token TYPE +%token END_TYPE +%token ARRAY +%token OF +%token STRUCT +%token END_STRUCT + + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +%type variable +%type symbolic_variable +/* helper symbol for prog_cnxn */ +%type any_symbolic_variable +%type variable_name + + + + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +/* Done totally within flex... + location_prefix + size_prefix +*/ +%token direct_variable_token +%type direct_variable + + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +%type multi_element_variable +/* helper symbol for any_symbolic_variable */ +%type any_multi_element_variable +%type array_variable +/* helper symbol for any_symbolic_variable */ +%type any_array_variable +%type subscripted_variable +/* helper symbol for any_symbolic_variable */ +%type any_subscripted_variable +%type subscript_list +%type subscript +%type structured_variable +/* helper symbol for any_symbolic_variable */ +%type any_structured_variable +%type record_variable +/* helper symbol for any_symbolic_variable */ +%type any_record_variable +%type field_selector + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ +%type input_declarations +/* helper symbol for input_declarations */ +%type input_declaration_list +%type input_declaration +%type edge_declaration +%type var_init_decl +%type var1_init_decl +%type var1_list +%type array_var_init_decl +%type structured_var_init_decl +%type fb_name_decl +/* helper symbol for fb_name_decl */ +%type fb_name_list_with_colon +/* helper symbol for fb_name_list_with_colon */ +%type var1_list_with_colon +// %type fb_name_list +// %type fb_name +%type output_declarations +%type input_output_declarations +/* helper symbol for input_output_declarations */ +%type var_declaration_list +%type var_declaration +%type temp_var_decl +%type var1_declaration +%type array_var_declaration +%type structured_var_declaration +%type var_declarations +%type retentive_var_declarations +%type located_var_declarations +/* helper symbol for located_var_declarations */ +%type located_var_decl_list +%type located_var_decl +%type external_var_declarations +/* helper symbol for external_var_declarations */ +%type external_declaration_list +%type external_declaration +%type global_var_name +%type global_var_declarations +/* helper symbol for global_var_declarations */ +%type global_var_decl_list +%type global_var_decl +%type global_var_spec +%type located_var_spec_init +%type location +%type global_var_list +%type string_var_declaration +%type single_byte_string_var_declaration +%type single_byte_string_spec +%type double_byte_string_var_declaration +%type double_byte_string_spec +%type incompl_located_var_declarations +/* helper symbol for incompl_located_var_declarations */ +%type incompl_located_var_decl_list +%type incompl_located_var_decl +%type incompl_location +%type var_spec +/* helper symbol for var_spec */ +%type string_spec +/* intermediate helper symbol for: + * - non_retentive_var_decls + * - output_declarations + */ +%type var_init_decl_list + +%token incompl_location_token + +%token VAR_INPUT +%token VAR_OUTPUT +%token VAR_IN_OUT +%token VAR_EXTERNAL +%token VAR_GLOBAL +%token END_VAR +%token RETAIN +%token NON_RETAIN +%token R_EDGE +%token F_EDGE +%token AT + + +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +//%type function_name +/* helper symbol for IL language */ +%type function_name_no_clashes +%type function_name_simpleop_clashes +//%type function_name_expression_clashes +/* helper symbols for ST language */ +//%type function_name_NOT_clashes +%type function_name_no_NOT_clashes + +//%type standard_function_name +/* helper symbols for IL language */ +%type standard_function_name_no_clashes +%type standard_function_name_simpleop_clashes +%type standard_function_name_expression_clashes +/* helper symbols for ST language */ +%type standard_function_name_NOT_clashes +%type standard_function_name_no_NOT_clashes + +%type derived_function_name +%type function_declaration +/* helper symbol for function_declaration */ +%type function_name_declaration +%type io_var_declarations +%type function_var_decls +%type function_body +%type var2_init_decl +/* intermediate helper symbol for function_declaration */ +%type io_OR_function_var_declarations_list +/* intermediate helper symbol for function_var_decls */ +%type var2_init_decl_list + +%token standard_function_name_token + +%token FUNCTION +%token END_FUNCTION +%token CONSTANT + + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ +%type function_block_type_name +%type standard_function_block_name +%type derived_function_block_name +%type function_block_declaration +%type other_var_declarations +%type temp_var_decls +%type non_retentive_var_decls +%type function_block_body +/* intermediate helper symbol for function_declaration */ +%type io_OR_other_var_declarations_list +/* intermediate helper symbol for temp_var_decls */ +%type temp_var_decls_list + +%token standard_function_block_name_token + +%token FUNCTION_BLOCK +%token END_FUNCTION_BLOCK +%token VAR_TEMP +%token END_VAR +%token VAR +%token NON_RETAIN +%token END_VAR + + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +%type program_type_name +%type program_declaration +/* helper symbol for program_declaration */ +%type program_var_declarations_list + +%token PROGRAM +%token END_PROGRAM + + +/********************************************/ +/* B 1.6 Sequential Function Chart elements */ +/********************************************/ +/* TODO */ +/* +%type sequential_function_chart +%type sfc_network +%type initial_step +%type step +%type action_association_list +%type step_name +%type action_association +/* helper symbol for action_association * +%type indicator_name_list +%type action_name +%type action_qualifier +%type timed_qualifier +%type action_time +%type indicator_name +%type transition +%type steps +%type step_name_list +%type transition_condition +%type action +*/ + +%token ASSIGN +%token ACTION +%token END_ACTION + +%token TRANSITION +%token END_TRANSITION +%token FROM +%token TO +%token PRIORITY + +%token INITIAL_STEP +%token STEP +%token END_STEP + +%token L +%token D +%token SD +%token DS +%token SL + +%token N +%token P +/* NOTE: the following two clash with the R and S IL operators. + * It will have to be handled when we include parsing of SFC... + */ +/* +%token R +%token S +*/ + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ +%type configuration_name +%type resource_type_name +%type configuration_declaration +// helper symbol for +// - configuration_declaration +// - resource_declaration +// +%type optional_global_var_declarations +// helper symbol for configuration_declaration +%type optional_access_declarations +// helper symbol for configuration_declaration +%type optional_instance_specific_initializations +// helper symbol for configuration_declaration +%type resource_declaration_list +%type resource_declaration +%type single_resource_declaration +// helper symbol for single_resource_declaration +%type task_configuration_list +// helper symbol for single_resource_declaration +%type program_configuration_list +%type resource_name +// %type access_declarations +// helper symbol for access_declarations +// %type access_declaration_list +// %type access_declaration +// %type access_path +// helper symbol for access_path +%type any_fb_name_list +%type global_var_reference +// %type access_name +%type program_output_reference +%type program_name +// %type direction +%type task_configuration +%type task_name +%type task_initialization +%type data_source +%type program_configuration +// helper symbol for program_configuration +%type optional_task_name +// helper symbol for program_configuration +%type optional_prog_conf_elements +%type prog_conf_elements +%type prog_conf_element +%type fb_task +%type prog_cnxn +%type prog_data_source +%type data_sink +%type instance_specific_initializations +// helper symbol for instance_specific_initializations +%type instance_specific_init_list +%type instance_specific_init +// helper symbol for instance_specific_init +%type fb_initialization + +%type prev_declared_global_var_name +%token prev_declared_global_var_name_token + +%type prev_declared_program_name +%token prev_declared_program_name_token + +%type prev_declared_resource_name +%token prev_declared_resource_name_token + +%token prev_declared_configuration_name_token + +// %type prev_declared_task_name +// %token prev_declared_task_name_token + +%token CONFIGURATION +%token END_CONFIGURATION +%token TASK +%token RESOURCE +%token ON +%token END_RESOURCE +%token VAR_CONFIG +%token VAR_ACCESS +%token END_VAR +%token WITH +%token PROGRAM +%token RETAIN +%token NON_RETAIN +%token PRIORITY +%token SINGLE +%token INTERVAL +%token READ_WRITE +%token READ_ONLY + + +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +%type instruction_list +%type il_instruction +%type il_incomplete_instruction +%type label +%type il_simple_operation +// helper symbol for il_simple_operation +%type il_simple_operator_clash_il_operand +%type il_expression +%type il_jump_operation +%type il_fb_call +%type il_formal_funct_call +// helper symbol for il_formal_funct_call +%type il_expr_operator_clash_eol_list +%type il_operand +%type il_operand_list +%type simple_instr_list +%type il_simple_instruction +%type il_param_list +%type il_param_instruction_list +%type il_param_instruction +%type il_param_last_instruction +%type il_param_assignment +%type il_param_out_assignment + +%token EOL + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +%token sendto_identifier_token +%type sendto_identifier + +%type LD_operator +%type LDN_operator +%type ST_operator +%type STN_operator +%type NOT_operator +%type S_operator +%type R_operator +%type S1_operator +%type R1_operator +%type CLK_operator +%type CU_operator +%type CD_operator +%type PV_operator +%type IN_operator +%type PT_operator +%type AND_operator +%type AND2_operator +%type OR_operator +%type XOR_operator +%type ANDN_operator +%type ANDN2_operator +%type ORN_operator +%type XORN_operator +%type ADD_operator +%type SUB_operator +%type MUL_operator +%type DIV_operator +%type MOD_operator +%type GT_operator +%type GE_operator +%type EQ_operator +%type LT_operator +%type LE_operator +%type NE_operator +%type CAL_operator +%type CALC_operator +%type CALCN_operator +%type RET_operator +%type RETC_operator +%type RETCN_operator +%type JMP_operator +%type JMPC_operator +%type JMPCN_operator + +%type il_simple_operator +%type il_simple_operator_clash +%type il_simple_operator_clash1 +%type il_simple_operator_clash2 +%type il_simple_operator_noclash + +//%type il_expr_operator +%type il_expr_operator_clash +%type il_expr_operator_noclash + +%type il_assign_operator +%type il_assign_out_operator +%type il_call_operator +%type il_return_operator +%type il_jump_operator + + +%token LD +%token LDN +%token ST +%token STN +%token NOT +%token S +%token R +%token S1 +%token R1 +%token CLK +%token CU +%token CD +%token PV +%token IN +%token PT +%token AND +%token AND2 /* character '&' in the source code*/ +%token OR +%token XOR +%token ANDN +%token ANDN2 /* characters '&N' in the source code */ +%token ORN +%token XORN +%token ADD +%token SUB +%token MUL +%token DIV +%token MOD +%token GT +%token GE +%token EQ +%token LT +%token LE +%token NE +%token CAL +%token CALC +%token CALCN +%token RET +%token RETC +%token RETCN +%token JMP +%token JMPC +%token JMPCN + +%token SENDTO /* "=>" */ + + +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +/* NOTE: + * + * - unary_operator, multiply_operator, + * add_operator and comparison_operator + * are not required. Their values are integrated + * directly into other rules... + */ +%type expression +%type xor_expression +%type and_expression +%type comparison +%type equ_expression +// %type comparison_operator +%type add_expression +// %type add_operator +%type term +// %type multiply_operator +%type power_expression +%type unary_expression +// %type unary_operator +%type primary_expression +/* intermediate helper symbol for primary_expression */ +%type function_invocation + +%token AND +%token XOR +%token OR +%token MOD +%token NOT +%token OPER_NE +%token OPER_GE +%token OPER_LE +%token OPER_EXP + + +/********************/ +/* B 3.2 Statements */ +/********************/ +%type statement_list +%type statement + + + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +%type assignment_statement +%token ASSIGN /* ":=" */ + + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +%type subprogram_control_statement +%type return_statement +%type fb_invocation +%type param_assignment +/* helper symbol for fb_invocation */ +%type param_assignment_list + +%token ASSIGN +%token SENDTO /* "=>" */ +%token RETURN + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ +%type selection_statement +%type if_statement +%type case_statement +%type case_element +%type case_list +%type case_list_element +/* helper symbol for if_statement */ +%type elseif_statement_list +/* helper symbol for elseif_statement_list */ +%type elseif_statement +/* helper symbol for case_statement */ +%type case_element_list + +%token IF +%token THEN +%token ELSIF +%token ELSE +%token END_IF + +%token CASE +%token OF +%token ELSE +%token END_CASE + + + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ +%type iteration_statement +%type for_statement +%type control_variable +%type while_statement +%type repeat_statement +%type exit_statement +/* Integrated directly into for_statement */ +// %type for_list + +%token FOR +%token ASSIGN +%token TO +%token BY +%token DO +%token END_FOR + +%token WHILE +%token DO +%token END_WHILE + +%token REPEAT +%token UNTIL +%token END_REPEAT + +%token EXIT + +%% + + + + +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ +/********************************************************/ + + + + + +/*****************************/ +/* Prelimenary constructs... */ +/*****************************/ +start: + library {$$ = $1;} +; + +/* the pragmas... */ +pragma: + pragma_token {$$ = new pragma_c($1);} + + + +/* NOTE: + * short version: + * identifier is used for previously undeclared identifiers + * any_identifier is used when any identifier, previously + * declared or not, is required in the syntax. + * + * long version: + * When flex comes across an identifier, it first + * searches through the currently declared variables, + * functions, types, etc... to determine if it has + * been previously declared. + * Only if the identifier has not yet been declared + * will it return an identifier_token (later turned into + * an identifier symbol by the bison generated syntax parser). + * + * Some constructs in the syntax, such as when calling + * a function 'F(var1 := 1; var2 := 2);', will accept _any_ + * identifier in 'var1', even if it has been previously + * declared in the current scope, since var1 belongs to + * another scope (the variables declared in function F). + * + * For the above reason, we need to define the symbol + * any_identifier. All the symbols that may become an + * any_identifier are expected to be stored in the + * abstract syntax as a identifier_c + */ +/* NOTE: + * Type names, function names, function block type names and + * program type names are considerd keywords once they are defined, + * so may no longer be used for variable names! + * BUT the spec is confusing on this issue, as it is not clear when + * a function name should be considered as defined. If it is to be + * considered defined only from the location from where it is declared + * and onwards, it means that before it is declared its name may be + * used for variable names! + * This means that we must allow names previously used for functions + * (et. al.) to also constitue an any_identifier! + */ +any_identifier: + identifier +| prev_declared_fb_name +| prev_declared_variable_name +/**/ +| prev_declared_enumerated_type_name +| prev_declared_simple_type_name +| prev_declared_subrange_type_name +| prev_declared_array_type_name +| prev_declared_structure_type_name +| prev_declared_string_type_name +| prev_declared_derived_function_name +| prev_declared_derived_function_block_name +| prev_declared_program_type_name +/**/ +| prev_declared_resource_name +| prev_declared_program_name +| prev_declared_global_var_name +; + + + + +prev_declared_variable_name: prev_declared_variable_name_token {$$ = new identifier_c($1);}; +prev_declared_fb_name: prev_declared_fb_name_token {$$ = new identifier_c($1);}; + +prev_declared_simple_type_name: prev_declared_simple_type_name_token {$$ = new identifier_c($1);}; +prev_declared_subrange_type_name: prev_declared_subrange_type_name_token {$$ = new identifier_c($1);}; +prev_declared_enumerated_type_name: prev_declared_enumerated_type_name_token {$$ = new identifier_c($1);}; +prev_declared_array_type_name: prev_declared_array_type_name_token {$$ = new identifier_c($1);}; +prev_declared_structure_type_name: prev_declared_structure_type_name_token {$$ = new identifier_c($1);}; +prev_declared_string_type_name: prev_declared_string_type_name_token {$$ = new identifier_c($1);}; + +prev_declared_derived_function_name: prev_declared_derived_function_name_token {$$ = new identifier_c($1);}; +prev_declared_derived_function_block_name: prev_declared_derived_function_block_name_token {$$ = new identifier_c($1);}; +prev_declared_program_type_name: prev_declared_program_type_name_token {$$ = new identifier_c($1);}; + + + + + +/***************************/ +/* B 0 - Programming Model */ +/***************************/ +library: + /* empty */ + {if (tree_root == NULL) + tree_root = new library_c(); + $$ = (list_c *)tree_root; + } +| library library_element_declaration + {$$ = $1; $$->add_element($2);} +| library error + {$$ = NULL; + print_err_msg(current_filename, @2.last_line, "unknown error."); + /* yychar */ + yyerrok; + } +; + + +library_element_declaration: + data_type_declaration +| function_declaration +| function_block_declaration +| program_declaration +| configuration_declaration +; + + + +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +/* NOTE: the spec defines identifier as: + * identifier ::= (letter|('_' (letter|digit))) {['_'] (letter|digit)} + * In essence, any sequence of letters or digits, starting with a letter + * or '_'. + * + * On section 2.1.3 (pg 26) , the spec states + * "The keywords listed in annex C shall not be used for any other purpose, + * e.g., variable names or extensions as defined in 1.5.1." + * (NOTE: the spec itself does not follow this rule, as it defines standard + * functions with names identidal to keywords, e.g. 'MOD', 'NOT' !!. This is + * another issue altogether, and is worked around somewhere else...) + * + * This means that we must re-define indentifier so as to exclude + * any keywords defined in annex C. + * + * Note also that the list includes + * - Data type names + * - Function names + * - Function Block names + * This means that any named used for a function name, data type name + * or function block name, essentially becomes a keyword, and may therefore + * no longer be re-used for any other use! (see NOTE 2) + * + * In our case, excluding the keywords is achieved in the lexical parser, + * by two mechanisms: + * (1) giving higher priority to the keywords (tokens) than to identifiers, + * so when the lexical parser finds a keyword it will be parsed as a + * token before being parsed as an identifier. + * (2) when an identifier is found that is not a keyword, the lexical parser + * then looks in the global symbol table, and will not return an identifier + * if the name has been previously used as a data type name, function name, + * or function block name! (In these cases it will return a + * prev_declared_function_name_token, etc...). + * + * Unfortunately, the language (especially IL) uses tokens that are + * not defined as keywords in the spec (e.g. 'IN', 'R1', 'S1', 'PT', etc...)! + * This means that it is valid to name a function 'IN', a variable 'PT', etc... + * BUT, the lexical parser will interpret these names as tokens (keywords). + * To allow these names to be used as function names, variable names, etc..., + * I (Mario) have augmented the definition of identifier to also include the tokens + * that are not explicitly defined as keywords in the spec!! + * + * + * + * + * NOTE 2: + * I (Mario) find it strange that the writers of the spec really want + * names previously used for function names, data type names or function + * block names, to become full fledged keywords. I understand that they + * do not want these names being used as variable names, but how about + * enumeration values? How about structure element names? + * If we interpret the spec literally, these would not be accepted, + * which would probably burden the programmer quite a bit, in making sure + * all these name don't clash! + * + * + * + * NOTE 3: The keywords, as specified in Annex C are... + * + * - Data type names + * - Function names + * - Function Block names + * - ACTION...END_ACTION + * - ARRAY...OF + * - AT + * - CASE...OF...ELSE...END_CASE + * - CONFIGURATION...END_CONFIGURATION + * - CONSTANT + * - EN, ENO + * - EXIT + * - FALSE + * - F_EDGE + * - FOR...TO...BY...DO...END_FOR + * - FUNCTION...END_FUNCTION + * - FUNCTION_BLOCK...END_FUNCTION_BLOCK + * - IF...THEN...ELSIF...ELSE...END_IF + * - INITIAL_STEP...END_STEP + * - NOT, MOD, AND, XOR, OR + * - PROGRAM...WITH... + * - PROGRAM...END_PROGRAM + * - R_EDGE + * - READ_ONLY, READ_WRITE + * - REPEAT...UNTIL...END_REPEAT + * - RESOURCE...ON...END_RESOURCE + * - RETAIN, NON_RETAIN + * - RETURN + * - STEP...END_STEP + * - STRUCT...END_STRUCT + * - TASK + * - TRANSITION...FROM...TO...END_TRANSITION + * - TRUE + * - TYPE...END_TYPE + * - VAR...END_VAR + * - VAR_INPUT...END_VAR + * - VAR_OUTPUT...END_VAR + * - VAR_IN_OUT...END_VAR + * - VAR_TEMP...END_VAR + * - VAR_EXTERNAL...END_VAR + * - VAR_ACCESS...END_VAR + * - VAR_CONFIG...END_VAR + * - VAR_GLOBAL...END_VAR + * - WHILE...DO...END_WHILE + * - WITH + */ + +identifier: + identifier_token {$$ = new identifier_c($1);} +/* Make sure that all tokens (names) not defined as keywords are included here... + * I (Mario) have already done this, but if any changes are made to this file, + * this list MUST be kept consistent!! + */ +/**/ +| PRIORITY {$$ = new identifier_c(strdup("PRIORITY"));} +| SINGLE {$$ = new identifier_c(strdup("SINGLE"));} +| INTERVAL {$$ = new identifier_c(strdup("INTERVAL"));} +/**/ +| LD_operator {$$ = il_operator_c_2_identifier_c($1);} +| LDN_operator {$$ = il_operator_c_2_identifier_c($1);} +| ST_operator {$$ = il_operator_c_2_identifier_c($1);} +| STN_operator {$$ = il_operator_c_2_identifier_c($1);} +| S_operator {$$ = il_operator_c_2_identifier_c($1);} +| R_operator {$$ = il_operator_c_2_identifier_c($1);} +| S1_operator {$$ = il_operator_c_2_identifier_c($1);} +| R1_operator {$$ = il_operator_c_2_identifier_c($1);} +| CLK_operator {$$ = il_operator_c_2_identifier_c($1);} +| CU_operator {$$ = il_operator_c_2_identifier_c($1);} +| CD_operator {$$ = il_operator_c_2_identifier_c($1);} +| PV_operator {$$ = il_operator_c_2_identifier_c($1);} +| IN_operator {$$ = il_operator_c_2_identifier_c($1);} +| PT_operator {$$ = il_operator_c_2_identifier_c($1);} +| ANDN_operator {$$ = il_operator_c_2_identifier_c($1);} +/* NOTE: ANDN2_operator corresponds to the string '&N' in the source code! + * This is __not__ a valid name, so it is omitted from this list!! + *| ANDN2_operator {$$ = il_operator_c_2_identifier_c($1);} + */ +/* NOTE: 'AND' is a keyword, so should not appear on this list... */ +| ORN_operator {$$ = il_operator_c_2_identifier_c($1);} +| XORN_operator {$$ = il_operator_c_2_identifier_c($1);} +| ADD_operator {$$ = il_operator_c_2_identifier_c($1);} +| SUB_operator {$$ = il_operator_c_2_identifier_c($1);} +| MUL_operator {$$ = il_operator_c_2_identifier_c($1);} +| DIV_operator {$$ = il_operator_c_2_identifier_c($1);} +| GT_operator {$$ = il_operator_c_2_identifier_c($1);} +| GE_operator {$$ = il_operator_c_2_identifier_c($1);} +| EQ_operator {$$ = il_operator_c_2_identifier_c($1);} +| LT_operator {$$ = il_operator_c_2_identifier_c($1);} +| LE_operator {$$ = il_operator_c_2_identifier_c($1);} +| NE_operator {$$ = il_operator_c_2_identifier_c($1);} +| CAL_operator {$$ = il_operator_c_2_identifier_c($1);} +| CALC_operator {$$ = il_operator_c_2_identifier_c($1);} +| CALCN_operator {$$ = il_operator_c_2_identifier_c($1);} +| RET_operator {$$ = il_operator_c_2_identifier_c($1);} +| RETC_operator {$$ = il_operator_c_2_identifier_c($1);} +| RETCN_operator {$$ = il_operator_c_2_identifier_c($1);} +| JMP_operator {$$ = il_operator_c_2_identifier_c($1);} +| JMPC_operator {$$ = il_operator_c_2_identifier_c($1);} +| JMPCN_operator {$$ = il_operator_c_2_identifier_c($1);} +/**/ +| L {$$ = new identifier_c(strdup("L"));} +| D {$$ = new identifier_c(strdup("D"));} +| SD {$$ = new identifier_c(strdup("SD"));} +| DS {$$ = new identifier_c(strdup("DS"));} +| SL {$$ = new identifier_c(strdup("SL"));} +| N {$$ = new identifier_c(strdup("N"));} +/* NOTE: the following two clash with the R and S IL operators. + * It will have to be handled when we include parsing of SFC... + */ +/* +| R {$$ = new identifier_c(strdup("R"));} +| S {$$ = new identifier_c(strdup("S"));} +*/ +| P {$$ = new identifier_c(strdup("P"));} + +; + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ +constant: + numeric_literal +| character_string +| time_literal +| bit_string_literal +| boolean_literal +/* NOTE: in order to remove reduce/reduce conflicts, + * unsigned_integer, signed_integer, binary_integer, octal_integer + * and hex_integer have been integrated directly into + * the constants construct, instead of belonging to + * either the bit_string_literal or integer_literal + * construct. + */ +/* NOTE: unsigned_integer, although used in some + * rules, is not defined in the spec! + * We therefore replaced unsigned_integer as integer + */ +/*| integer {} */ /* i.e. an unsigned_integer */ /* NOTE: already included as a signed integer! */ +| signed_integer +| binary_integer +| octal_integer +| hex_integer +; + +/* a helper symbol for expression */ +/* A constant without any preceding '-', but may + * include a preceding '+' ! + */ +non_negative_constant: + non_negative_numeric_literal +| character_string +| time_literal +| bit_string_literal +| boolean_literal +| non_negative_signed_integer +| binary_integer +| octal_integer +| hex_integer +; + + + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ +/* NOTES: + * + * - integer is parsed by flex, but signed_integer + * is parsed by bison. Flex cannot parse a signed + * integer correctly! For example: '123+456' + * would be parsed by flex as an {integer} {signed_integer} + * instead of {integer} '+' {integer} + * + * - Neither flex nor bison can parse a real_literal + * completely (and correctly). + * Note that we cannot use the definition of real in bison as + * real: signed_integer '.' integer [exponent] + * exponent: {'E'|'e'} ['+'|'-'] integer + * because 123e45 would be parsed by flex as + * integer (123) identifier (e45). + * I.e., flex never hands over an 'e' directly to + * bison, but rather interprets it as an identifier. + * I guess we could jump through hoops and get it + * working in bison, but the following alternative + * seems more straight forward... + * + * We therefore had to break up the definition of + * real_literal in discrete parts: + * real_literal: [real_type_name '#'] singned_real + * signed_real: ['+'|'-'] real + * Flex handles real, while bison handles signed_real + * and real_literal. + * + * - According to the spec, intger '.' integer + * may be reduced to either a real or a fixed_point. + * It is nevertheless possible to figure out from the + * context which of the two rules should be used in + * the reduction. + * Unfortunately, due to the issue described above + * regarding the exponent of a real, the syntax + * integer '.' integer + * must be parsed by flex as a single token (i.e. + * fixed_point_token). This means we must add fixed_point + * to the definition of real! + * + * - The syntax also uses a construct + * fixed_point: integer ['.' integer] + * Notice that real is not defined based on fixed point, + * but rather off integer thus: + * real: integer '.' integer [exponent] + * This means that a real may not be composed of a single + * integer, unlike the construct fixed_point! + * This also means that a + * integer '.' integer + * could be reduced to either a real or a fixed_point + * construct. It is probably possible to decide by looking + * at the context, BUT: + * Unfortunatley, due to the reasons explained way above, + * a real (with an exponent) has to be handled by flex as a + * whole. This means that we cannot leave to bison (the syntax + * parser) the decision of how to reduce an + * integer '.' integer + * (either to real or to fixed_point) + * The decision on how to reduce it would need to be done by + * ther lexical analyser (i.e. flex). But flex cannot do this + * sort of thing. + * The solution I (Mario) adopted is to have flex return + * a real_token on (notice that exponent is no longer optional) + * integer '.' integer exponent + * and to return a fixed_point_token when it finds + * integer '.' integer + * We now redefine real and fixed_point to be + * fixed_point: fixed_point_token | integer + * real: real_token | fixed_point_token + */ +real: + real_token {$$ = new real_c($1);} +| fixed_point_token {$$ = new real_c($1);} +; + +integer: integer_token {$$ = new integer_c($1);}; +binary_integer: binary_integer_token {$$ = new binary_integer_c($1);}; +octal_integer: octal_integer_token {$$ = new octal_integer_c($1);}; +hex_integer: hex_integer_token {$$ = new hex_integer_c($1);}; + +numeric_literal: + integer_literal +| real_literal +; + +/* helper symbol for non_negative_constant */ +non_negative_numeric_literal: + integer_literal +| non_negative_real_literal +; + + +integer_literal: + integer_type_name '#' signed_integer + {$$ = new integer_literal_c($1, $3);} +| integer_type_name '#' binary_integer + {$$ = new integer_literal_c($1, $3);} +| integer_type_name '#' octal_integer + {$$ = new integer_literal_c($1, $3);} +| integer_type_name '#' hex_integer + {$$ = new integer_literal_c($1, $3);} +/* NOTE: see note in the definition of constant for reason + * why signed_integer, binary_integer, octal_integer + * and hex_integer are missing here! + */ +; + +signed_integer: + integer +| '+' integer {$$ = $2;} +| '-' integer {$$ = new neg_expression_c($2);} +; + +/* a helper symbol for non_negative_constant */ +/* A integer without any preceding '-', but may + * include a preceding '+' ! + */ +non_negative_signed_integer: + integer +| '+' integer {$$ = $2;} +; + + +real_literal: + signed_real +| real_type_name '#' signed_real + {$$ = new real_literal_c($1, $3);} +; + +/* helper symbol for non_negative_numeric_literal */ +non_negative_real_literal: + non_negative_signed_real +| real_type_name '#' signed_real + {$$ = new real_literal_c($1, $3);} +; + +signed_real: + real +| '+' real {$$ = $2;} +| '-' real {$$ = new neg_expression_c($2);} +; + +/* helper symbol for non_negative_real_literal */ +non_negative_signed_real: + real +| '+' real {$$ = $2;} +; + + +bit_string_literal: + bit_string_type_name '#' integer /* i.e. unsigned_integer */ + {$$ = new bit_string_literal_c($1, $3);} +| bit_string_type_name '#' binary_integer + {$$ = new bit_string_literal_c($1, $3);} +| bit_string_type_name '#' octal_integer + {$$ = new bit_string_literal_c($1, $3);} +| bit_string_type_name '#' hex_integer + {$$ = new bit_string_literal_c($1, $3);} +/* NOTE: see note in the definition of constant for reason + * why unsigned_integer, binary_integer, octal_integer + * and hex_integer are missing here! + */ +/* NOTE: see note under the B 1.2.1 section of token + * and grouping type definition for reason why the use of + * bit_string_type_name, although seemingly incorrect, is + * really correct here! + */ +; + + +boolean_literal: + TRUE {$$ = new boolean_literal_c(new bool_type_name_c(), + new boolean_true_c());} +| FALSE {$$ = new boolean_literal_c(new bool_type_name_c(), + new boolean_false_c());} +/* +| BOOL '#' '1' {} +| BOOL '#' '0' {} +*/ +/* NOTE: the rules + * BOOL '#' '1' + * and + * BOOL '#' '0' + * do not work as expected... + * Consider that we are using 'BOOL' and '#' as tokens + * that flex hands over to bison (yacc). Because flex would + * then parse the single '1' or '0' as an integer, + * the rule in bison would have to be + * BOOL '#' integer, followed by verifying of the + * integer has the correct value! + * + * We therefore have flex return TRUE whenever it + * comes across 'TRUE' or 'BOOL#1', and FALSE whenever + * it comes across 'FALSE' or 'BOOL#0'. + * Note that this means that flex will parse "BOOL#01" + * as FALSE followed by an integer ('1'). + * Bison should detect this as an error, so we should + * be OK. + * + * Another option would be to change the rules to accept + * BOOL '#' integer + * but then check whether the integer has a correct + * value! At the moment I feel that the first option + * is more straight forward. + */ +; + + + +/*******************************/ +/* B 1.2.2 - Character Strings */ +/*******************************/ +/* Transform the tokens given us by flex into leafs */ +single_byte_character_string: single_byte_character_string_token + {$$ = new single_byte_character_string_c($1);}; + +double_byte_character_string: double_byte_character_string_token + {$$ = new double_byte_character_string_c($1);}; + + +character_string: + single_byte_character_string +| double_byte_character_string +; + + + + + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +time_literal: + time_of_day +| date +| date_and_time +| duration +; + + +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +duration: +/* (T | TIME) '#' ['-'] interval */ +/* NOTE: since TIME is also a data type, it is a keyword + * and may therefore be handled by a token. + * + * Unfortunately T is not a data type, and therefore + * not a keyword. This means that we may have variables named T! + * Flex cannot return the token TIME when it comes across a single T! + * + * We therefore have flex returning the token T_SHARP + * when it comes across 'T#' + */ + TIME '#' interval + {$$ = new duration_c(NULL, $3);} +| TIME '#' '-' interval + {$$ = new duration_c(new neg_time_c(), $4);} +| T_SHARP interval + {$$ = new duration_c(NULL, $2);} +| T_SHARP '-' interval + {$$ = new duration_c(new neg_time_c(), $3);} +; + + +interval: + days +| hours +| minutes +| seconds +| milliseconds +; + +integer_d: integer_d_token {$$ = new integer_c($1);}; +integer_h: integer_h_token {$$ = new integer_c($1);}; +integer_m: integer_m_token {$$ = new integer_c($1);}; +integer_s: integer_s_token {$$ = new integer_c($1);}; +integer_ms: integer_ms_token {$$ = new integer_c($1);}; + +fixed_point_d: + fixed_point_d_token + {$$ = new fixed_point_c($1);} +| integer_d +; + +fixed_point_h: + fixed_point_h_token + {$$ = new fixed_point_c($1);} +| integer_h +; + +fixed_point_m: + fixed_point_m_token + {$$ = new fixed_point_c($1);} +| integer_m +; + +fixed_point_s: + fixed_point_s_token + {$$ = new fixed_point_c($1);} +| integer_s +; + +fixed_point_ms: + fixed_point_ms_token + {$$ = new fixed_point_c($1);} +| integer_ms +; + + +fixed_point: + fixed_point_token + {$$ = new fixed_point_c($1);} +| integer +; + + +days: +/* fixed_point ('d') */ + fixed_point_d + {$$ = new days_c($1, NULL);} +/*| integer ('d') ['_'] hours */ +| integer_d hours + {$$ = new days_c($1, $2);} +| integer_d '_' hours + {$$ = new days_c($1, $3);} +; + + +hours: +/* fixed_point ('h') */ + fixed_point_h + {$$ = new hours_c($1, NULL);} +/*| integer ('h') ['_'] minutes */ +| integer_h minutes + {$$ = new hours_c($1, $2);} +| integer_h '_' minutes + {$$ = new hours_c($1, $3);} +; + +minutes: +/* fixed_point ('m') */ + fixed_point_m + {$$ = new minutes_c($1, NULL);} +/*| integer ('m') ['_'] seconds */ +| integer_m seconds + {$$ = new minutes_c($1, $2);} +| integer_m '_' seconds + {$$ = new minutes_c($1, $3);} +; + +seconds: +/* fixed_point ('s') */ + fixed_point_s + {$$ = new seconds_c($1, NULL);} +/*| integer ('s') ['_'] milliseconds */ +| integer_s milliseconds + {$$ = new seconds_c($1, $2);} +| integer_s '_' milliseconds + {$$ = new seconds_c($1, $3);} +; + +milliseconds: +/* fixed_point ('ms') */ + fixed_point_ms + {$$ = new milliseconds_c($1);} +; + + + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ +time_of_day: + TIME_OF_DAY '#' daytime + {$$ = new time_of_day_c($3);} +; + + +daytime: + day_hour ':' day_minute ':' day_second + {$$ = new daytime_c($1, $3, $5);} +; + + +day_hour: integer; +day_minute: integer; +day_second: fixed_point; + + +date: + DATE '#' date_literal + {$$ = new date_c($3);} +| D_SHARP date_literal + {$$ = new date_c($2);} +; + + +date_literal: + year '-' month '-' day + {$$ = new date_literal_c($1, $3, $5);} +; + + +year: integer; +month: integer; +day: integer; + + +date_and_time: + DATE_AND_TIME '#' date_literal '-' daytime + {$$ = new date_and_time_c($3, $5);} +; + + + + + + +/**********************/ +/* B 1.3 - Data Types */ +/**********************/ +/* Strangely, the following symbol does seem to be required! */ +/* +data_type_name: + non_generic_type_name +| generic_type_name +; +*/ + +non_generic_type_name: + elementary_type_name +| derived_type_name +; + + + +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ +elementary_type_name: + numeric_type_name +| date_type_name +| bit_string_type_name +| elementary_string_type_name +| TIME {$$ = new time_type_name_c();} +| BOOL {$$ = new bool_type_name_c();} +/* NOTE: see note under the B 1.2.1 section of token + * and grouping type definition for reason why BOOL + * was added to this definition. + */ +; + +numeric_type_name: + integer_type_name +| real_type_name +; + +integer_type_name: + signed_integer_type_name +| unsigned_integer_type_name +; + +signed_integer_type_name: + SINT {$$ = new sint_type_name_c();} +| INT {$$ = new int_type_name_c();} +| DINT {$$ = new dint_type_name_c();} +| LINT {$$ = new lint_type_name_c();} +; + +unsigned_integer_type_name: + USINT {$$ = new usint_type_name_c();} +| UINT {$$ = new uint_type_name_c();} +| UDINT {$$ = new udint_type_name_c();} +| ULINT {$$ = new ulint_type_name_c();} +; + +real_type_name: + REAL {$$ = new real_type_name_c();} +| LREAL {$$ = new lreal_type_name_c();} +; + +date_type_name: + DATE {$$ = new date_type_name_c();} +| TIME_OF_DAY {$$ = new tod_type_name_c();} +| TOD {$$ = new tod_type_name_c();} +| DATE_AND_TIME {$$ = new dt_type_name_c();} +| DT {$$ = new dt_type_name_c();} +; + + +bit_string_type_name: + BYTE {$$ = new byte_type_name_c();} +| WORD {$$ = new word_type_name_c();} +| DWORD {$$ = new dword_type_name_c();} +| LWORD {$$ = new lword_type_name_c();} +/* NOTE: see note under the B 1.2.1 section of token + * and grouping type definition for reason why the BOOL + * was omitted from this definition. + */ +; + + +/* Helper symbol to concentrate the instantiation + * of STRING and WSTRING into a single location. + * + * These two elements show up in several other rules, + * but we want to create the equivalent abstract syntax + * in a single location of this file, in order to make + * possible future changes easier to edit... + */ +elementary_string_type_name: + STRING {$$ = new string_type_name_c();} +| WSTRING {$$ = new wstring_type_name_c();} +; + + + +/********************************/ +/* B 1.3.2 - Generic data types */ +/********************************/ +/* Strangely, the following symbol does seem to be required! */ +/* +generic_type_name: + ANY +| ANY_DERIVED +| ANY_ELEMENTARY +| ANY_MAGNITUDE +| ANY_NUM +| ANY_REAL +| ANY_INT +| ANY_BIT +| ANY_STRING +| ANY_DATE +; +*/ + + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + +derived_type_name: + single_element_type_name +| prev_declared_array_type_name {$$ = $1;} +| prev_declared_structure_type_name {$$ = $1;} +| prev_declared_string_type_name {$$ = $1;} +; + +single_element_type_name: + prev_declared_simple_type_name {$$ = $1;} +/* Include the following if arrays of function blocks are to be allowed! + * Since the standard does not allow them, + * we leave it commented out for the time being... + */ +//| prev_declared_derived_function_block_name {$$ = $1;} +| prev_declared_subrange_type_name {$$ = $1;} +| prev_declared_enumerated_type_name {$$ = $1;} +; + +/* NOTE: in order to remove a reduce/reduce conflict, + * all occurences of simple_type_name, etc... + * have been replaced with identifier! + */ +/* +simple_type_name: identifier; +subrange_type_name: identifier; +enumerated_type_name: identifier; +array_type_name: identifier; +structure_type_name: identifier; +*/ + +data_type_declaration: + TYPE type_declaration_list END_TYPE + {$$ = new data_type_declaration_c($2);} +; + +/* helper symbol for data_type_declaration */ +type_declaration_list: + type_declaration ';' + {$$ = new type_declaration_list_c(); $$->add_element($1);} +| type_declaration_list type_declaration ';' + {$$ = $1; $$->add_element($2);} +; + +type_declaration: + single_element_type_declaration +| array_type_declaration +| structure_type_declaration +| string_type_declaration +; + +single_element_type_declaration: + simple_type_declaration +| subrange_type_declaration +| enumerated_type_declaration +; + +simple_type_declaration: +/* simple_type_name ':' simple_spec_init */ + identifier ':' simple_spec_init + {$$ = new simple_type_declaration_c($1, $3); + library_element_symtable.insert($1, prev_declared_simple_type_name_token); + } +; + + +simple_spec_init: + simple_specification + /* The following line was changed so that we wouldn't + * have the first element of a simple_spec_init_c() + * pointing to another simple_spec_init_c! + */ +/* +| simple_specification ASSIGN constant + {$$ = new simple_spec_init_c($1, $3);} +*/ +| elementary_type_name ASSIGN constant + {$$ = new simple_spec_init_c($1, $3);} +| prev_declared_simple_type_name ASSIGN constant + {$$ = new simple_spec_init_c($1, $3);} +; + +/* When converting to C/C++, we need to know whether + * the elementary_type_name is being used in a variable + * declaration or elsewhere (ex. declaration of a derived + * type), so the abstract syntax has the elementary_type_name + * wrapped inside a simple_spec_init_c. + * The exact same thing occurs with prev_declared_simple_type_name. + * + * This is why in the definition of simple_spec_init, + * simple_specification was brocken up into its + * constituent components... + */ +simple_specification: +// elementary_type_name | simple_type_name + elementary_type_name + {$$ = new simple_spec_init_c($1, NULL);} +| prev_declared_simple_type_name + {$$ = new simple_spec_init_c($1, NULL);} +; + + +subrange_type_declaration: +/* subrange_type_name ':' subrange_spec_init */ + identifier ':' subrange_spec_init + {$$ = new subrange_type_declaration_c($1, $3); + library_element_symtable.insert($1, prev_declared_subrange_type_name_token); + } +; + +subrange_spec_init: + subrange_specification + {$$ = new subrange_spec_init_c($1, NULL);} +| subrange_specification ASSIGN signed_integer + {$$ = new subrange_spec_init_c($1, $3);} +; + +subrange_specification: + integer_type_name '(' subrange')' + {$$ = new subrange_specification_c($1, $3);} +| prev_declared_subrange_type_name {$$ = $1;} +; + + +subrange: + signed_integer DOTDOT signed_integer + {$$ = new subrange_c($1, $3);} +; + +enumerated_type_declaration: +/* enumerated_type_name ':' enumerated_spec_init */ + identifier ':' enumerated_spec_init + {$$ = new enumerated_type_declaration_c($1, $3); + library_element_symtable.insert($1, prev_declared_enumerated_type_name_token); + } +; + + +enumerated_spec_init: + enumerated_specification + {$$ = new enumerated_spec_init_c($1, NULL);} +| enumerated_specification ASSIGN enumerated_value + {$$ = new enumerated_spec_init_c($1, $3);} +; + +enumerated_specification: + '(' enumerated_value_list ')' + {$$ = $2;} +| prev_declared_enumerated_type_name {$$ = $1;} +; + +/* helper symbol for enumerated_specification */ +enumerated_value_list: + enumerated_value + {$$ = new enumerated_value_list_c(); $$->add_element($1);} +| enumerated_value_list ',' enumerated_value + {$$ = $1; $$->add_element($3);} +; + + +enumerated_value: + identifier + {$$ = $1;} +| prev_declared_enumerated_type_name '#' any_identifier + {$$ = new enumerated_value_c($1, $3);} +; + + + +array_type_declaration: +/* array_type_name ':' array_spec_init */ + identifier ':' array_spec_init + {$$ = new array_type_declaration_c($1, $3); + library_element_symtable.insert($1, prev_declared_array_type_name_token); + } +; + +array_spec_init: + array_specification + {$$ = new array_spec_init_c($1, NULL);} +| array_specification ASSIGN array_initialization + {$$ = new array_spec_init_c($1, $3);} +; + + +array_specification: + prev_declared_array_type_name + {$$ = $1;} +| ARRAY '[' array_subrange_list ']' OF non_generic_type_name + {$$ = new array_specification_c($3, $6);} +; + +/* helper symbol for array_specification */ +array_subrange_list: + subrange + {$$ = new array_subrange_list_c(); $$->add_element($1);} +| array_subrange_list ',' subrange + {$$ = $1; $$->add_element($1);} +; + + +array_initialization: + '[' array_initial_elements_list ']' + {$$ = $2;} +; + + +/* helper symbol for array_initialization */ +array_initial_elements_list: + array_initial_elements + {$$ = new array_initial_elements_list_c(); $$->add_element($1);} +| array_initial_elements_list ',' array_initial_elements + {$$ = $1; $$->add_element($3);} +; + + +array_initial_elements: + array_initial_element +| integer '(' ')' +| integer '(' array_initial_element ')' + {$$ = new array_initial_elements_c($1, $3);} +; + + +array_initial_element: + constant +| enumerated_value +| structure_initialization +| array_initialization +; + + + +structure_type_declaration: +/* structure_type_name ':' structure_specification */ + identifier ':' structure_specification + {$$ = new structure_type_declaration_c($1, $3); + library_element_symtable.insert($1, prev_declared_structure_type_name_token); + } +; + + +structure_specification: + structure_declaration +| initialized_structure +; + + +initialized_structure: + prev_declared_structure_type_name + {$$ = new initialized_structure_c($1, NULL);} +| prev_declared_structure_type_name ASSIGN structure_initialization + {$$ = new initialized_structure_c($1, $3);} +; + + +structure_declaration: + STRUCT structure_element_declaration_list END_STRUCT + {$$ = $2;} +; + +/* helper symbol for structure_declaration */ +structure_element_declaration_list: + structure_element_declaration ';' + {$$ = new structure_element_declaration_list_c(); $$->add_element($1);} +| structure_element_declaration_list structure_element_declaration ';' + {$$ = $1; $$->add_element($2);} +; + + +structure_element_declaration: + structure_element_name ':' simple_spec_init + {$$ = new structure_element_declaration_c($1, $3);} +| structure_element_name ':' subrange_spec_init + {$$ = new structure_element_declaration_c($1, $3);} +| structure_element_name ':' enumerated_spec_init + {$$ = new structure_element_declaration_c($1, $3);} +| structure_element_name ':' array_spec_init + {$$ = new structure_element_declaration_c($1, $3);} +| structure_element_name ':' initialized_structure + {$$ = new structure_element_declaration_c($1, $3);} +; + + +structure_element_name: any_identifier; + + +structure_initialization: + '(' structure_element_initialization_list ')' + {$$ = $2;} +; + +/* helper symbol for structure_initialization */ +structure_element_initialization_list: + structure_element_initialization + {$$ = new structure_element_initialization_list_c(); $$->add_element($1);} +| structure_element_initialization_list ',' structure_element_initialization + {$$ = $1; $$->add_element($3);} +; + + +structure_element_initialization: + structure_element_name ASSIGN constant + {$$ = new structure_element_initialization_c($1, $3);} +| structure_element_name ASSIGN enumerated_value + {$$ = new structure_element_initialization_c($1, $3);} +| structure_element_name ASSIGN array_initialization + {$$ = new structure_element_initialization_c($1, $3);} +| structure_element_name ASSIGN structure_initialization + {$$ = new structure_element_initialization_c($1, $3);} +; + +/* NOTE: in order to remove a reduce/reduce conflict, + * all occurences of string_type_name + * have been replaced with identifier! + */ +/* +string_type_name: identifier; +*/ + +string_type_declaration: +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ + identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init + {$$ = new string_type_declaration_c($1, $3, $4, $5); + library_element_symtable.insert($1, prev_declared_string_type_name_token); + } +; + + +/* helper symbol for string_type_declaration */ +string_type_declaration_size: + '[' integer ']' + {$$ = $2;} +/* REMOVED !! */ +//| /* empty */ +// {$$ = NULL;} +; +/* The syntax contains a reduce/reduce conflict. + * The optional '[' ']' + * has been changed to become mandatory to remove the conflict. + * + * The conflict arises because + * new_str_type : STRING := "hello!" + * may be reduced to a string_type_declaration OR + * a simple_type_declaration. + * + * Our change forces it to be reduced to a + * simple_type_declaration! + * We chose this option because changing the definition + * of simple_spec_init would force us to change all the other + * rules in which it appears. The change we made has no + * side-effects! + */ + +/* helper symbol for string_type_declaration */ +string_type_declaration_init: + /* empty */ + {$$ = NULL;} +| ASSIGN character_string + {$$ = $2;} +; + + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +variable: + symbolic_variable +| direct_variable +; + + +symbolic_variable: +/* NOTE: To be entirely correct, variable_name should be replacemed by + * prev_declared_variable_name | prev_declared_fb_name | prev_declared_global_var_name + */ + prev_declared_variable_name + {$$ = new symbolic_variable_c($1);} +| prev_declared_fb_name + {$$ = new symbolic_variable_c($1);} +| prev_declared_global_var_name + {$$ = new symbolic_variable_c($1);} +| multi_element_variable +; + + +/* NOTE: in section B 1.7, when configuring a program, symbolic_variable + * is used. Nevertheless, during the parsing of a configuration, + * the variables in question are out of scope, so we should + * be allowing any_identifier instead of prev_declared_variable_name! + * + * We therefore need a new any_symbolic_variable construct that + * allows the use of any_identifier instead of previously declared + * variables, function blocks, etc... + */ +any_symbolic_variable: +// variable_name -> replaced by any_identifier + any_identifier + {$$ = new symbolic_variable_c($1);} +| any_multi_element_variable +; + + +/* for yet undeclared variable names ! */ +variable_name: identifier; + + + + + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +direct_variable: direct_variable_token {$$ = new direct_variable_c($1);}; + + + + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +multi_element_variable: + array_variable +| structured_variable +; + +/* please see note above any_symbolic_variable */ +any_multi_element_variable: + any_array_variable +| any_structured_variable +; + + +array_variable: + subscripted_variable '[' subscript_list ']' + {$$ = new array_variable_c($1, $3);} +; + +/* please see note above any_symbolic_variable */ +any_array_variable: + any_subscripted_variable '[' subscript_list ']' + {$$ = new array_variable_c($1, $3);} +; + + +subscripted_variable: + symbolic_variable +; + + +/* please see note above any_symbolic_variable */ +any_subscripted_variable: + any_symbolic_variable +; + + +subscript_list: + subscript + {$$ = new subscript_list_c(); $$->add_element($1);} +| subscript_list ',' subscript + {$$ = $1; $$->add_element($3);} +; + + +subscript: expression; + + +structured_variable: + record_variable '.' field_selector + {$$ = new structured_variable_c($1, $3);} +; + + +/* please see note above any_symbolic_variable */ +any_structured_variable: + any_record_variable '.' field_selector + {$$ = new structured_variable_c($1, $3);} +; + + + +record_variable: + symbolic_variable +; + + +/* please see note above any_symbolic_variable */ +any_record_variable: + any_symbolic_variable +; + + +field_selector: any_identifier; + + + + + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ +input_declarations: + VAR_INPUT input_declaration_list END_VAR + {$$ = new input_declarations_c(NULL, $2);} +| VAR_INPUT RETAIN input_declaration_list END_VAR + {$$ = new input_declarations_c(new retain_option_c(), $3);} +| VAR_INPUT NON_RETAIN input_declaration_list END_VAR + {$$ = new input_declarations_c(new non_retain_option_c(), $3);} +; + +/* helper symbol for input_declarations */ +input_declaration_list: + input_declaration ';' + {$$ = new input_declaration_list_c(); $$->add_element($1);} +| input_declaration_list input_declaration ';' + {$$ = $1; $$->add_element($2);} +; + + +input_declaration: + var_init_decl +| edge_declaration +; + + +edge_declaration: + var1_list ':' BOOL R_EDGE + {$$ = new edge_declaration_c(new raising_edge_option_c(), $1);} +| var1_list ':' BOOL F_EDGE + {$$ = new edge_declaration_c(new falling_edge_option_c(), $1);} +; + + +var_init_decl: + var1_init_decl +| array_var_init_decl +| structured_var_init_decl +| fb_name_decl +| string_var_declaration +; + + + + +var1_init_decl: + var1_list ':' simple_spec_init + {$$ = new var1_init_decl_c($1, $3);} +| var1_list ':' subrange_spec_init + {$$ = new var1_init_decl_c($1, $3);} +| var1_list ':' enumerated_spec_init + {$$ = new var1_init_decl_c($1, $3);} +; + + +var1_list: + variable_name + {$$ = new var1_list_c(); $$->add_element($1); + variable_name_symtable.insert($1, prev_declared_variable_name_token); + } + | var1_list ',' variable_name + {$$ = $1; $$->add_element($3); + variable_name_symtable.insert($3, prev_declared_variable_name_token); + } +; + + + +array_var_init_decl: + var1_list ':' array_spec_init + {$$ = new array_var_init_decl_c($1, $3);} +; + + +structured_var_init_decl: + var1_list ':' initialized_structure + {$$ = new structured_var_init_decl_c($1, $3);} +; + + +/* NOTE: see notes above fb_name_list and var1_list + * for reason why ':' was removed from this rule! + * In essence, to remove a shift/reduce conflict, + * the ':' was moved to var1_list and fb_name_list! + */ +fb_name_decl: +/* fb_name_list ':' function_block_type_name */ + fb_name_list_with_colon function_block_type_name + {$$ = new fb_name_decl_c($1, $2, NULL);} +/*| fb_name_list ':' function_block_type_name ASSIGN structure_initialization */ +| fb_name_list_with_colon function_block_type_name ASSIGN structure_initialization + {$$ = new fb_name_decl_c($1, $2, $4);} +; + + + +/* NOTE: In order to remove a reduce/reduce conflict between + * var1_list and fb_name_list, which are identical to each + * other, fb_name_list has been redefined to be a var1_list. + * + * In order to remove a further shift/reduce conflict, var1_list + * is imediately transfomred into var1_list_with_colon + * (i.e. it includes the ':' following the list), which + * means that fb_name_list is built from a + * var1_list_with_colon after all! + */ +/* +fb_name_list: + (* fb_name *) + identifier + {$$ = new fb_name_list_c($1); + variable_name_symtable.insert($1, prev_declared_fb_name_token); + } +(* | fb_name_list ',' fb_name *) +| fb_name_list ',' identifier + {$$ = $1; $$->add_element($3); + variable_name_symtable.insert($3, prev_declared_fb_name_token); + } +; +*/ + +fb_name_list_with_colon: + var1_list_with_colon + {$$ = new fb_name_list_c(); + /* fill up the new fb_name_list_c object with the references + * contained in the var1_list_c object. + */ + FOR_EACH_ELEMENT(elem, $1, {$$->add_element(elem);}); + delete $1; + /* change the tokens associated with the symbols stored in + * the variable name symbol table from prev_declared_variable_name_token + * to prev_declared_fb_name_token + */ + FOR_EACH_ELEMENT(elem, $$, {variable_name_symtable.set(elem, prev_declared_fb_name_token);}); + } +; + +/* helper symbol for fb_name_list_with_colon */ +var1_list_with_colon: + var1_list ':' +; + + +// fb_name: identifier; + + + +output_declarations: + VAR_OUTPUT var_init_decl_list END_VAR + {$$ = new output_declarations_c(NULL, $2);} +| VAR_OUTPUT RETAIN var_init_decl_list END_VAR + {$$ = new output_declarations_c(new retain_option_c(), $3);} +| VAR_OUTPUT NON_RETAIN var_init_decl_list END_VAR + {$$ = new output_declarations_c(new non_retain_option_c(), $3);} +; + + + +input_output_declarations: + VAR_IN_OUT var_declaration_list END_VAR + {$$ = new input_output_declarations_c($2);} +; + + + +/* helper symbol for input_output_declarations */ +var_declaration_list: + var_declaration ';' + {$$ = new var_declaration_list_c(); $$->add_element($1);} +| var_declaration_list var_declaration ';' + {$$ = $1; $$->add_element($2);} +; + + +var_declaration: + temp_var_decl +| fb_name_decl +; + + +temp_var_decl: + var1_declaration +| array_var_declaration +| structured_var_declaration +| string_var_declaration +; + +var1_declaration: + var1_list ':' simple_specification + {$$ = new var1_init_decl_c($1, $3);} +| var1_list ':' subrange_specification + {$$ = new var1_init_decl_c($1, $3);} +| var1_list ':' enumerated_specification + {$$ = new var1_init_decl_c($1, $3);} +; + + + +array_var_declaration: + var1_list ':' array_specification + {$$ = new array_var_declaration_c($1, $3);} +; + +structured_var_declaration: + var1_list ':' prev_declared_structure_type_name + {$$ = new structured_var_declaration_c($1, $3);} +; + + +var_declarations: + VAR var_init_decl_list END_VAR + {$$ = new var_declarations_c(NULL, $2);} +| VAR CONSTANT var_init_decl_list END_VAR + {$$ = new var_declarations_c(new constant_option_c(), $3);} +; + + +retentive_var_declarations: + VAR RETAIN var_init_decl_list END_VAR + {$$ = new retentive_var_declarations_c($3);} +; + + +located_var_declarations: + VAR located_var_decl_list END_VAR + {$$ = new located_var_declarations_c(NULL, $2);} +| VAR CONSTANT located_var_decl_list END_VAR + {$$ = new located_var_declarations_c(new constant_option_c(), $3);} +| VAR RETAIN located_var_decl_list END_VAR + {$$ = new located_var_declarations_c(new retain_option_c(), $3);} +| VAR NON_RETAIN located_var_decl_list END_VAR + {$$ = new located_var_declarations_c(new non_retain_option_c(), $3);} +; + + +/* helper symbol for located_var_declarations */ +located_var_decl_list: + located_var_decl ';' + {$$ = new located_var_decl_list_c(); $$->add_element($1);} +| located_var_decl_list located_var_decl ';' + {$$ = $1; $$->add_element($2);} +; + + +located_var_decl: + variable_name location ':' located_var_spec_init + {$$ = new located_var_decl_c($1, $2, $4); + variable_name_symtable.insert($1, prev_declared_variable_name_token); + } +| location ':' located_var_spec_init + {$$ = new located_var_decl_c(NULL, $1, $3);} +; + + + + +external_var_declarations: + VAR_EXTERNAL external_declaration_list END_VAR + {$$ = new external_var_declarations_c(NULL, $2);} +| VAR_EXTERNAL CONSTANT external_declaration_list END_VAR + {$$ = new external_var_declarations_c(new constant_option_c(), $3);} +; + +/* helper symbol for external_var_declarations */ +external_declaration_list: + external_declaration ';' + {$$ = new external_declaration_list_c(); $$->add_element($1);} +| external_declaration_list external_declaration';' + {$$ = $1; $$->add_element($2);} +; + + +external_declaration: + global_var_name ':' simple_specification + {$$ = new external_declaration_c($1, $3); + variable_name_symtable.insert($1, prev_declared_variable_name_token); + } +| global_var_name ':' subrange_specification + {$$ = new external_declaration_c($1, $3); + variable_name_symtable.insert($1, prev_declared_variable_name_token); + } +| global_var_name ':' enumerated_specification + {$$ = new external_declaration_c($1, $3); + variable_name_symtable.insert($1, prev_declared_variable_name_token); + } +| global_var_name ':' array_specification + {$$ = new external_declaration_c($1, $3); + variable_name_symtable.insert($1, prev_declared_variable_name_token); + } +| global_var_name ':' prev_declared_structure_type_name + {$$ = new external_declaration_c($1, $3); + variable_name_symtable.insert($1, prev_declared_variable_name_token); + } +| global_var_name ':' function_block_type_name + {$$ = new external_declaration_c($1, $3); + variable_name_symtable.insert($1, prev_declared_fb_name_token); + } +; + + +global_var_name: identifier; + + +global_var_declarations: + VAR_GLOBAL global_var_decl_list END_VAR + {$$ = new global_var_declarations_c(NULL, $2);} +| VAR_GLOBAL CONSTANT global_var_decl_list END_VAR + {$$ = new global_var_declarations_c(new constant_option_c(), $3);} +| VAR_GLOBAL RETAIN global_var_decl_list END_VAR + {$$ = new global_var_declarations_c(new retain_option_c(), $3);} +; + + +/* helper symbol for global_var_declarations */ +global_var_decl_list: + global_var_decl ';' + {$$ = new global_var_decl_list_c(); $$->add_element($1);} +| global_var_decl_list global_var_decl ';' + {$$ = $1; $$->add_element($2);} +; + + +global_var_decl: + global_var_spec ':' + {$$ = new global_var_decl_c($1, NULL);} +| global_var_spec ':' located_var_spec_init + {$$ = new global_var_decl_c($1, $3);} +| global_var_spec ':' function_block_type_name + {$$ = new global_var_decl_c($1, $3);} +; + + +global_var_spec: + global_var_list {$$ = $1;} +| location +| global_var_name location + {$$ = new global_var_spec_c($1, $2); + variable_name_symtable.insert($1, prev_declared_global_var_name_token); + } + +; + + +located_var_spec_init: + simple_spec_init +| subrange_spec_init +| enumerated_spec_init +| array_spec_init +| initialized_structure +| single_byte_string_spec +| double_byte_string_spec +; + + +location: + AT direct_variable + {$$ = new location_c($2);} +; + + + +global_var_list: + global_var_name + {$$ = new global_var_list_c(); $$->add_element($1); + variable_name_symtable.insert($1, prev_declared_global_var_name_token); + } +| global_var_list ',' global_var_name + {$$ = $1; $$->add_element($3); + variable_name_symtable.insert($3, prev_declared_global_var_name_token); + } +; + + + +string_var_declaration: + single_byte_string_var_declaration +| double_byte_string_var_declaration +; + +single_byte_string_var_declaration: + var1_list ':' single_byte_string_spec + {$$ = new single_byte_string_var_declaration_c($1, $3);} +; + +/* NOTE: The constructs + * + * [W]STRING + * and + * [W]STRING ASSIGN single_byte_character_string + * + * were removed as they are already contained + * within a other constructs. + * + * single_byte_string_spec is used in: + * - single_byte_string_var_declaration -> + * -> string_var_declaration ---> var_init_decl + * |--> temp_var_decl + * |--> var2_init_decl + * - located_var_spec_init + * + * STRING [ASSIGN string_constant] -> elementary_string_type_name -> + * -> simple_spec -> simple_specification -> simple_spec_init -> + * -> located_var_spec_init + * + * STRING [ASSIGN string_constant] -> elementary_string_type_name -> + * -> simple_spec -> simple_specification -> simple_spec_init -> + * -> var1_init_decl -> var_init_decl + * + * STRING [ASSIGN string_constant] -> elementary_string_type_name -> + * -> simple_spec -> simple_specification -> simple_spec_init -> + * -> var1_init_decl -> var2_init_decl + * + * STRING [ASSIGN string_constant] -> elementary_string_type_name -> + * -> simple_spec -> simple_specification -> + * -> var1_declaration -> temp_var_decl + */ +single_byte_string_spec: +/* STRING + {$$ = new single_byte_string_spec_c(NULL, NULL);} +*/ + STRING '[' integer ']' + {$$ = new single_byte_string_spec_c($3, NULL);} +/* +| STRING ASSIGN single_byte_character_string + {$$ = new single_byte_string_spec_c(NULL, $3);} +*/ +| STRING '[' integer ']' ASSIGN single_byte_character_string + {$$ = new single_byte_string_spec_c($3, $6);} +; + + +double_byte_string_var_declaration: + var1_list ':' double_byte_string_spec + {$$ = new double_byte_string_var_declaration_c($1, $3);} +; + +double_byte_string_spec: +/* WSTRING + {$$ = new double_byte_string_spec_c(NULL, NULL);} +*/ + WSTRING '[' integer ']' + {$$ = new double_byte_string_spec_c($3, NULL);} +/* +| WSTRING ASSIGN double_byte_character_string + {$$ = new double_byte_string_spec_c(NULL, $3);} +*/ +| WSTRING '[' integer ']' ASSIGN double_byte_character_string + {$$ = new double_byte_string_spec_c($3, $6);} +; + + + +incompl_located_var_declarations: + VAR incompl_located_var_decl_list END_VAR + {$$ = new incompl_located_var_declarations_c(NULL, $2);} +| VAR RETAIN incompl_located_var_decl_list END_VAR + {$$ = new incompl_located_var_declarations_c(new retain_option_c(), $3);} +| VAR NON_RETAIN incompl_located_var_decl_list END_VAR + {$$ = new incompl_located_var_declarations_c(new non_retain_option_c(), $3);} +; + +/* helper symbol for incompl_located_var_declarations */ +incompl_located_var_decl_list: + incompl_located_var_decl ';' + {$$ = new incompl_located_var_decl_list_c(); $$->add_element($1);} +| incompl_located_var_decl_list incompl_located_var_decl ';' + {$$ = $1; $$->add_element($2);} +; + + +incompl_located_var_decl: + variable_name incompl_location ':' var_spec + {$$ = new incompl_located_var_decl_c($1, $2, $4);} +; + + +incompl_location: + AT incompl_location_token + {$$ = new incompl_location_c($2);} +; + + +var_spec: + simple_specification +| subrange_specification +| enumerated_specification +| array_specification +| prev_declared_structure_type_name +| string_spec +; + + +/* helper symbol for var_spec */ +/* NOTE: The constructs + * + * STRING + * and + * WSTRING + * + * were removed as they are already contained + * within a simple_specification. + */ +string_spec: +/* STRING + {$$ = new single_byte_string_spec_c(NULL, NULL);} +*/ + STRING '[' integer ']' + {$$ = new single_byte_string_spec_c($3, NULL);} +/* +| WSTRING + {$$ = new double_byte_string_spec_c(NULL, NULL);} +*/ +| WSTRING '[' integer ']' + {$$ = new double_byte_string_spec_c($3, NULL);} +; + + + + +/* intermediate helper symbol for: + * - non_retentive_var_decls + * - output_declarations + * - var_declarations + */ +var_init_decl_list: + var_init_decl ';' + {$$ = new var_init_decl_list_c(); $$->add_element($1);} +| var_init_decl_list var_init_decl ';' + {$$ = $1; $$->add_element($2);} +; + + + + +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +/* The following rules should be set such as: + * function_name: function_name_no_clashes | function_name_simpleop_clashes | function_name_expression_clashes + * function_name: function_name_no_NOT_clashes | function_name_NOT_clashes; + */ + +function_name_no_clashes: prev_declared_derived_function_name | standard_function_name_no_clashes; +function_name_simpleop_clashes: standard_function_name_simpleop_clashes; +//function_name_expression_clashes: standard_function_name_expression_clashes; + +function_name_no_NOT_clashes: prev_declared_derived_function_name | standard_function_name_no_NOT_clashes; +//function_name_NOT_clashes: standard_function_name_NOT_clashes; + +/* +function_name: + prev_declared_derived_function_name +| standard_function_name +; +*/ + +/* NOTE: The list of standard function names + * includes the standard functions MOD(), NOT() + * + * Strangely enough, MOD and NOT are reserved keywords, + * so shouldn't be used for function names. + * + * The specification contradicts itself! + * Our workaround is to treat MOD as a token, + * but to include this token as a + * standard_function_name. + * + * The names of all other standard functions get + * preloaded into the library_element_symbol_table + * with the token value of + * standard_function_name_token + * Actually, simply for completeness, MOD is also + * loaded into the library_element_symbol_table, but + * it is irrelevant since flex will catch MOD as a + * token, before it interprets it as an identifier, + * and looks in the library_element_symbol_table to check + * whether it has been previously declared. + * + * NOTE: The same as the above also occurs with the IL + * operators NOT AND OR XOR ADD SUB MUL DIV MOD + * GT GE EQ LT LE NE. + * Note that MOD is once again in the list! + * Anyway, we give these the same treatement as + * MOD, since we are writing a parser for ST and + * IL simultaneously. If this were not the case, + * the ST parser would not need the tokens NOT AND ... + * + * NOTE: Note that 'NOT' is special, as it conflicts + * with two operators: the IL 'NOT' operator, and + * the unary operator 'NOT' in ST!! + * + * NOTE: The IL language is ambiguous, since using NOT, AND, ... + * may be interpreted as either an IL operator, or + * as a standard function call! + * I (Mario) opted to interpret it as an IL operator. + * This requires changing the syntax for IL language + * function calling, to exclude all function with + * names that clash with IL operators. I therefore + * created the constructs + * function_name_without_clashes + * standard_function_name_without_clashes + * to include all function names, except those that clash + * with IL operators. These constructs are only used + * within the IL language! + */ +/* The following rules should be set such as: + * standard_function_name: standard_function_name_no_clashes | standard_function_name_simpleop_clashes | standard_function_name_expression_clashes + * standard_function_name: standard_function_name_no_NOT_clashes | standard_function_name_NOT_clashes; + */ + +/* +standard_function_name: + standard_function_name_no_clashes +| standard_function_name_expression_clashes +| standard_function_name_NOT_clashes +//| standard_function_name_simpleop_only_clashes +; +*/ + +standard_function_name_no_NOT_clashes: + standard_function_name_no_clashes +| standard_function_name_expression_clashes +//| standard_function_name_simpleop_only_clashes +; + +standard_function_name_no_clashes: + standard_function_name_token + {$$ = new identifier_c($1);} +; + + +standard_function_name_simpleop_clashes: + standard_function_name_NOT_clashes +//| standard_function_name_simpleop_only_clashes +; + +standard_function_name_NOT_clashes: + NOT + {$$ = new identifier_c(strdup("NOT"));} +; + +/* Add here any other IL simple operators that collide + * with standard function names! + * Don't forget to uncomment the equivalent lines in + * - standard_function_name_simpleop_clashes + * - standard_function_name + * - standard_function_name_no_NOT_clashes + */ +/* +standard_function_name_simpleop_only_clashes: +; +*/ + +standard_function_name_expression_clashes: + AND_operator {$$ = il_operator_c_2_identifier_c($1);} +//NOTE: AND2 (corresponding to the source code string '&') does not clash +// with a standard function name, so should be commented out! +//| AND2_operator {$$ = il_operator_c_2_identifier_c($1);} +| OR_operator {$$ = il_operator_c_2_identifier_c($1);} +| XOR_operator {$$ = il_operator_c_2_identifier_c($1);} +| ADD_operator {$$ = il_operator_c_2_identifier_c($1);} +| SUB_operator {$$ = il_operator_c_2_identifier_c($1);} +| MUL_operator {$$ = il_operator_c_2_identifier_c($1);} +| DIV_operator {$$ = il_operator_c_2_identifier_c($1);} +| MOD_operator {$$ = il_operator_c_2_identifier_c($1);} +| GT_operator {$$ = il_operator_c_2_identifier_c($1);} +| GE_operator {$$ = il_operator_c_2_identifier_c($1);} +| EQ_operator {$$ = il_operator_c_2_identifier_c($1);} +| LT_operator {$$ = il_operator_c_2_identifier_c($1);} +| LE_operator {$$ = il_operator_c_2_identifier_c($1);} +| NE_operator {$$ = il_operator_c_2_identifier_c($1);} +; + + +derived_function_name: + identifier +| prev_declared_derived_function_name + {$$ = $1; + if (not(allow_function_overloading)) + ERROR; + } +; + + +function_declaration: +/* FUNCTION derived_function_name ':' elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */ + function_name_declaration ':' elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION + {$$ = new function_declaration_c($1, $3, $4, $5); + variable_name_symtable.pop(); + if (allow_function_overloading) { + switch (library_element_symtable.find_value($1)) { + case prev_declared_derived_function_name_token: + /* do nothing, already in map. */ + break; + case BOGUS_TOKEN_ID: + /* Not yet in map. Must insert...*/ + library_element_symtable.insert($1, prev_declared_derived_function_name_token); + break; + default: + /* Already in map but associated with something else other than a funtion name! */ + ERROR; + } + } else { + library_element_symtable.insert($1, prev_declared_derived_function_name_token); + } + } +/* | FUNCTION derived_function_name ':' derived_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */ +| function_name_declaration ':' derived_type_name io_OR_function_var_declarations_list function_body END_FUNCTION + {$$ = new function_declaration_c($1, $3, $4, $5); + variable_name_symtable.pop(); + if (allow_function_overloading) { + switch (library_element_symtable.find_value($1)) { + case prev_declared_derived_function_name_token: /* do nothing, already in map. */ break; + case BOGUS_TOKEN_ID: library_element_symtable.insert($1, prev_declared_derived_function_name_token); break; + default: ERROR; + } + } else { + library_element_symtable.insert($1, prev_declared_derived_function_name_token); + } + } +; + +/* helper symbol for function_declaration */ +/* NOTE: due to reduce/reduce conflicts between identifiers + * being reduced to either a variable or an enumerator value, + * we were forced to keep a symbol table of the names + * of all declared variables. Variables are no longer + * created from simple identifier_token, but from + * prev_declared_variable_name_token. + * + * BUT, in functions the function name itself may be used as + * a variable! In order to be able to parse this correctly, + * the token parser (flex) must return a prev_declared_variable_name_token + * when it comes across the function name, while parsing + * the function itself. + * We do this by inserting the function name into the variable + * symbol table, and having flex return a prev_declared_variable_name_token + * whenever it comes across it. + * When we finish parsing the function the variable name + * symbol table is cleared of all entries, and the function + * name is inserted into the library element symbol table. This + * means that from then onwards flex will return a + * derived_function_name_token whenever it comes across the + * function name. + * + * In order to insert the function name into the variable_name + * symbol table BEFORE the function body gets parsed, we + * need the parser to reduce a construct that contains the + * the function name. That is why we created this extra + * construct (function_name_declaration), i.e. to force + * the parser to reduce it, before parsing the function body! + */ +function_name_declaration: + FUNCTION derived_function_name + {$$ = $2; + /* the function name functions as a + * variable within the function itself! + * + * Remember that the variable_name_symtable + * is cleared once the end of the function + * is parsed. + */ + variable_name_symtable.insert($2, prev_declared_variable_name_token); + } +; + + + +/* intermediate helper symbol for function_declaration */ +io_OR_function_var_declarations_list: + /* empty */ + {$$ = new var_declarations_list_c();} +| io_OR_function_var_declarations_list io_var_declarations + {$$ = $1; $$->add_element($2);} +| io_OR_function_var_declarations_list function_var_decls + {$$ = $1; $$->add_element($2);} +; + + +io_var_declarations: + input_declarations +| output_declarations +| input_output_declarations +; + + +function_var_decls: + VAR CONSTANT var2_init_decl_list END_VAR + {$$ = new function_var_decls_c(new constant_option_c(), $3);} +| VAR var2_init_decl_list END_VAR + {$$ = new function_var_decls_c(NULL, $2);} +; + +/* intermediate helper symbol for function_var_decls */ +var2_init_decl_list: + var2_init_decl ';' + {$$ = new var2_init_decl_list_c(); $$->add_element($1);} +| var2_init_decl_list var2_init_decl ';' + {$$ = $1; $$->add_element($2);} +; + + +function_body: + statement_list {$$ = $1;} /* if we leave it for the default action we get a type clash! */ +| instruction_list {$$ = $1;} /* if we leave it for the default action we get a type clash! */ +/* +| ladder_diagram +| function_block_diagram +*/ +; + + +var2_init_decl: + var1_init_decl +| array_var_init_decl +| structured_var_init_decl +| string_var_declaration +; + + + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ +function_block_type_name: + prev_declared_derived_function_block_name +| standard_function_block_name +; + + +standard_function_block_name: standard_function_block_name_token {$$ = new identifier_c($1);}; + +derived_function_block_name: identifier; + + +function_block_declaration: + FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations_list function_block_body END_FUNCTION_BLOCK + {$$ = new function_block_declaration_c($2, $3, $4); + library_element_symtable.insert($2, prev_declared_derived_function_block_name_token); + /* Clear the variable_name_symtable. Since + * we have finished parsing the function block, + * the variable names are now out of scope, so + * are no longer valid! + */ + variable_name_symtable.pop(); + } +; + + + +/* intermediate helper symbol for function_declaration */ +/* { io_var_declarations | other_var_declarations } */ +/* + * NOTE: we re-use the var_declarations_list_c + */ +io_OR_other_var_declarations_list: + /* empty */ + {$$ = new var_declarations_list_c();} +| io_OR_other_var_declarations_list io_var_declarations + {$$ = $1; $$->add_element($2);} +| io_OR_other_var_declarations_list other_var_declarations + {$$ = $1; $$->add_element($2);} +; + +/* NOTE: + * The IEC specification gives the following definition: + * other_var_declarations ::= + * external_var_declarations + * | var_declarations + * | retentive_var_declarations + * | non_retentive_var_declarations + * | temp_var_decls + * | incompl_located_var_declarations + * + * Nvertheless, the symbol non_retentive_var_declarations + * is not defined in the spec. This seems to me (Mario) + * to be a typo, so non_retentive_var_declarations + * has been replaced with non_retentive_var_decls + * in the following rule! + */ +other_var_declarations: + temp_var_decls +| non_retentive_var_decls +| external_var_declarations +| var_declarations +| retentive_var_declarations +| incompl_located_var_declarations +; + + +temp_var_decls: + VAR_TEMP temp_var_decls_list END_VAR + {$$ = new temp_var_decls_c($2);} +; + + +/* intermediate helper symbol for temp_var_decls */ +temp_var_decls_list: + temp_var_decl ';' + {$$ = new temp_var_decls_list_c(); $$->add_element($1);} +| temp_var_decls_list temp_var_decl ';' + {$$ = $1; $$->add_element($2);} +; + + +non_retentive_var_decls: + VAR NON_RETAIN var_init_decl_list END_VAR + {$$ = new non_retentive_var_decls_c($3);} +; + + + +function_block_body: + statement_list {$$ = $1;} +| instruction_list {$$ = $1;} +/* +| sequential_function_chart +| ladder_diagram +| function_block_diagram +| +*/ +; + + + + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +program_type_name: identifier; + + +program_declaration: + PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM + {$$ = new program_declaration_c($2, $3, $4); + library_element_symtable.insert($2, prev_declared_program_type_name_token); + /* Clear the variable_name_symtable. Since + * we have finished parsing the program declaration, + * the variable names are now out of scope, so + * are no longer valid! + */ + variable_name_symtable.pop(); + } +; + + +/* helper symbol for program_declaration */ +/* + * NOTE: we re-use the var_declarations_list_c + */ +program_var_declarations_list: + /* empty */ + {$$ = new var_declarations_list_c();} +| program_var_declarations_list io_var_declarations + {$$ = $1; $$->add_element($2);} +| program_var_declarations_list other_var_declarations + {$$ = $1; $$->add_element($2);} +| program_var_declarations_list located_var_declarations + {$$ = $1; $$->add_element($2);} +/* +| program_var_declarations_list program_access_decls + {$$ = $1; $$->add_element($2);} +*/ +; + + +/* TODO ... */ +/* +program_access_decls: + VAR_ACCESS program_access_decl_list END_VAR +; +*/ + +/* helper symbol for program_access_decls */ +/* +program_access_decl_list: + program_access_decl ';' +| program_access_decl_list program_access_decl ';' +; +*/ + +/* +program_access_decl: + access_name ':' symbolic_variable ':' non_generic_type_name +| access_name ':' symbolic_variable ':' non_generic_type_name direction +; +*/ + + + +/********************************************/ +/* B 1.6 Sequential Function Chart elements */ +/********************************************/ +/* TODO ... */ + +/* +sequential_function_chart: + sfc_network +| sequential_function_chart sfc_network +; + +sfc_network: + initial_step +| sfc_network step +| sfc_network transition +| sfc_network action +; + +initial_step: + INITIAL_STEP step_name ':' action_association_list END_STEP +; + +step: + STEP step_name ':' action_association_list END_STEP +; + +/* helper symbol for: + * - initial_step + * - step + * +action_association_list: + /* empty * +| action_association_list action_association ';' +; + +step_name: identifier; + +action_association: + action_name '(' action_qualifier indicator_name_list ')' +; + +/* helper symbol for action_association * +indicator_name_list: + /* empty * +| indicator_name_list ',' indicator_name +; + +action_name: identifier; + +action_qualifier: + /* empty * +| N +| R +| S +| P +| timed_qualifier ',' action_time +; + +timed_qualifier: + L +| D +| SD +| DS +| SL +; + +action_time: + duration +| variable_name +; + +indicator_name: variable_name; + +transition: + TRANSITION FROM steps TO steps transition_condition END_TRANSITION +| TRANSITION transition_name FROM steps TO steps transition_condition END_TRANSITION +| TRANSITION '(' PRIORITY ASSIGN integer ')' FROM steps TO steps transition_condition END_TRANSITION +| TRANSITION transition_name '(' PRIORITY ASSIGN integer ')' FROM steps TO steps transition_condition END_TRANSITION +; + +transition_name: identifier; + +steps: + step_name +| '(' step_name_list ')' +; + + +step_name_list: + step_name ',' step_name +| step_name_list ',' step_name +; + + +transition_condition: + ':' simple_instruction_list +| ASSIGN expression ';' +| ':' fbd_network +| ':' rung +; + +action: + ACTION action_name ':' function_block_body END_ACTION +; +*/ + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ +/* NOTE: + * It is not clear from reading the specification to which namespace + * the names of resources, tasks and programs belong to. + * + * The following syntax assumes that resource and program names belong to the + * same namespace as the variables defined within + * the resource/configuration (i.e. VAR_GLOBAL). + * Task names belong to a namespace all of their own, since they don't + * produce conflicts in the syntax parser, so we might just as well + * leave them be! ;-) + * The above decision was made taking into + * account that inside a VAR_CONFIG declaration global variables + * may be referenced starting off from the resource name as: + * resource_name.program_name.variable_name + * Notice how resource names and program names are used in a very similar + * manner as are variable names. + * Using a single namespace for all the above mentioned names + * also makes it easier to write the syntax parser!! ;-) Using a private + * namespace for each of the name types (resource names, program names, + * global varaiable names), i.e. letting the names be re-used across + * each of the groups (resource, program, global variables), produces + * reduce/reduce conflicts in the syntax parser. Actually, it is only + * the resource names that need to be distinguished into a + * prev_delcared_resource_name so as not to conflict with [gloabl] variable + * names in the 'data' construct. + * The program names are only tracked to make sure that two programs do not + * get the same name. + * + * Using a single namespace does have the drawback that the user will + * not be able to re-use names for resources or programs if these + * have already been used to name a variable! + * + * If it ever becomes necessary to change this interpretation of + * the syntax, then this section of the syntax parser must be updated! + */ +prev_declared_global_var_name: prev_declared_global_var_name_token {$$ = new identifier_c($1);}; +prev_declared_resource_name: prev_declared_resource_name_token {$$ = new identifier_c($1);}; +prev_declared_program_name: prev_declared_program_name_token {$$ = new identifier_c($1);}; +// prev_declared_task_name: prev_declared_task_name_token {$$ = new identifier_c($1);}; + + + + + + +configuration_name: identifier; + +/* NOTE: The specification states that valid resource type names + * are implementation defined, i.e. each implementaion will define + * what resource types it supports. + * We are implementing this syntax parser to be used by any + * implementation, so at the moment we accept any identifier + * as a resource type name. + * This implementation should probably be changed in the future. We + * should probably have a resource_type_name_token, and let the + * implementation load the global symbol library with the + * accepted resource type names before parsing the code. + * + */ +resource_type_name: any_identifier; + +configuration_declaration: + CONFIGURATION configuration_name + optional_global_var_declarations + single_resource_declaration + {variable_name_symtable.pop();} + optional_access_declarations + optional_instance_specific_initializations + END_CONFIGURATION + {$$ = new configuration_declaration_c($2, $3, $4, $6, $7); + library_element_symtable.insert($2, prev_declared_configuration_name_token); + variable_name_symtable.pop(); + } +| CONFIGURATION configuration_name + optional_global_var_declarations + resource_declaration_list + optional_access_declarations + optional_instance_specific_initializations + END_CONFIGURATION + {$$ = new configuration_declaration_c($2, $3, $4, $5, $6); + library_element_symtable.insert($2, prev_declared_configuration_name_token); + variable_name_symtable.pop(); + } +| CONFIGURATION error END_CONFIGURATION + {$$ = NULL; + print_err_msg(current_filename, @2.last_line, "error in configuration declaration."); + /* yychar */ + yyerrok; + } +; + +// helper symbol for +// - configuration_declaration +// - resource_declaration +// +optional_global_var_declarations: + // empty + {$$ = NULL;} +| global_var_declarations +; + + +// helper symbol for configuration_declaration // +optional_access_declarations: + // empty + {$$ = NULL;} +//| access_declarations +; + +// helper symbol for configuration_declaration // +optional_instance_specific_initializations: + // empty + {$$ = NULL;} +| instance_specific_initializations +; + +// helper symbol for configuration_declaration // +resource_declaration_list: + resource_declaration + {$$ = new resource_declaration_list_c(); $$->add_element($1);} +| resource_declaration_list resource_declaration + {$$ = $1; $$->add_element($2);} +; + + +resource_declaration: + RESOURCE {variable_name_symtable.push();} resource_name ON resource_type_name + optional_global_var_declarations + single_resource_declaration + END_RESOURCE + {$$ = new resource_declaration_c($3, $5, $6, $7); + variable_name_symtable.pop(); + variable_name_symtable.insert($3, prev_declared_resource_name_token); + } +; + + +single_resource_declaration: + task_configuration_list program_configuration_list + {$$ = new single_resource_declaration_c($1, $2);} +; + + +// helper symbol for single_resource_declaration // +task_configuration_list: + // empty + {$$ = new task_configuration_list_c();} +| task_configuration_list task_configuration ';' + {$$ = $1; $$->add_element($2);} +; + + +// helper symbol for single_resource_declaration // +program_configuration_list: + program_configuration ';' + {$$ = new program_configuration_list_c(); $$->add_element($1);} +| program_configuration_list program_configuration ';' + {$$ = $1; $$->add_element($2);} +; + + +resource_name: identifier; + +/* +access_declarations: + VAR_ACCESS access_declaration_list END_VAR + {$$ = NULL;} +; + +// helper symbol for access_declarations // +access_declaration_list: + access_declaration ';' +| access_declaration_list access_declaration ';' +; + + +access_declaration: + access_name ':' access_path ':' non_generic_type_name +| access_name ':' access_path ':' non_generic_type_name direction +; + + +access_path: + direct_variable +| prev_delcared_resource_name '.' direct_variable +| any_fb_name_list symbolic_variable +| prev_delcared_resource_name '.' any_fb_name_list symbolic_variable +| prev_delcared_program_name '.' any_fb_name_list symbolic_variable +| prev_delcared_resource_name '.' prev_delcared_program_name '.' any_fb_name_list symbolic_variable +; +*/ + +// helper symbol for +// - access_path +// - instance_specific_init +// +/* NOTE: The fb_name_list refers to funtion block variables + * that have been declared in a scope outside the one we are + * currently parsing, so we must accept them to be any_identifier! + * + * Beware that other locations of this syntax parser also require + * a fb_name_list. In those locations the function blocks are being declared, + * so only currently un-used identifiers (i.e. identifier) may be accepted. + * + * In order to distinguish the two, here we use any_fb_name_list, while + * in the the locations we simply use fb_name_list! + */ +any_fb_name_list: + // empty + {$$ = new any_fb_name_list_c();} +//| fb_name_list fb_name '.' +| any_fb_name_list any_identifier '.' + {$$ = $1; $$->add_element($2);} +; + + + +global_var_reference: +// [resource_name '.'] global_var_name ['.' structure_element_name] // + prev_declared_global_var_name + {$$ = new global_var_reference_c(NULL, $1, NULL);} +| prev_declared_global_var_name '.' structure_element_name + {$$ = new global_var_reference_c(NULL, $1, $3);} +| prev_declared_resource_name '.' prev_declared_global_var_name + {$$ = new global_var_reference_c($1, $3, NULL);} +| prev_declared_resource_name '.' prev_declared_global_var_name '.' structure_element_name + {$$ = new global_var_reference_c($1, $3, $5);} +; + + +//access_name: identifier; + + +program_output_reference: +/* NOTE: + * program_output_reference is merely used within data_source. + * data_source is merely used within task_initialization + * task_initialization appears in a configuration declaration + * _before_ the programs are declared, so we cannot use + * prev_declared_program_name, as what might seem correct at first. + * + * The semantic checker must later check whether the identifier + * used really refers to a program declared after the task + * initialization! + */ +// prev_declared_program_name '.' symbolic_variable + program_name '.' symbolic_variable + {$$ = new program_output_reference_c($1, $3);} +; + +program_name: identifier; + +/* +direction: + READ_WRITE + {$$ = NULL;} +| READ_ONLY + {$$ = NULL;} +; +*/ + +task_configuration: + TASK task_name task_initialization + {$$ = new task_configuration_c($2, $3);} +; + +/* NOTE: The specification does nopt mention the namespace to which task names + * should belong to. Unlike resource and program names, for the moment we + * let the task names belong to their own private namespace, as they do not + * produce any conflicts in the syntax parser. + * If in the future our interpretation of the spec. turns out to be incorrect, + * the definition of task_name may have to be changed! + */ +task_name: any_identifier; + +task_initialization: +// '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' // + '(' PRIORITY ASSIGN integer ')' + {$$ = new task_initialization_c(NULL, NULL, $4);} +| '(' SINGLE ASSIGN data_source ',' PRIORITY ASSIGN integer ')' + {$$ = new task_initialization_c($4, NULL, $8);} +| '(' INTERVAL ASSIGN data_source ',' PRIORITY ASSIGN integer ')' + {$$ = new task_initialization_c(NULL, $4, $8);} +| '(' SINGLE ASSIGN data_source ',' INTERVAL ASSIGN data_source ',' PRIORITY ASSIGN integer ')' + {$$ = new task_initialization_c($4, $8, $12);} +; + +data_source: + constant +| global_var_reference +| program_output_reference +| direct_variable +; + +program_configuration: +// PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] // + PROGRAM program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements + {$$ = new program_configuration_c(NULL, $2, $3, $5, $6); + variable_name_symtable.insert($2, prev_declared_program_name_token); + } +| PROGRAM RETAIN program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements + {$$ = new program_configuration_c(new retain_option_c(), $3, $4, $6, $7); + variable_name_symtable.insert($3, prev_declared_program_name_token); + } +| PROGRAM NON_RETAIN program_name optional_task_name ':' prev_declared_program_type_name optional_prog_conf_elements + {$$ = new program_configuration_c(new non_retain_option_c(), $3, $4, $6, $7); + variable_name_symtable.insert($3, prev_declared_program_name_token); + } +; + +// helper symbol for program_configuration // +optional_task_name: + // empty // + {$$ = NULL;} +| WITH task_name + {$$ = $2;} +; + +// helper symbol for program_configuration // +optional_prog_conf_elements: + // empty // + {$$ = NULL;} +| '(' prog_conf_elements ')' + {$$ = $2;} +; + + +prog_conf_elements: + prog_conf_element + {$$ = new prog_conf_elements_c(); $$->add_element($1);} +| prog_conf_elements ',' prog_conf_element + {$$ = $1; $$->add_element($3);} +; + + +prog_conf_element: + fb_task +| prog_cnxn +; + + +fb_task: + // fb_name WITH task_name +/* NOTE: The fb_name refers to funtion block variables + * that have been declared in a scope outside the one we are + * currently parsing, so we must accept them to be any_identifier! + */ + any_identifier WITH task_name + {$$ = new fb_task_c($1, $3);} +; + + +/* NOTE: + * The semantics of configuring a program are rather confusing, so here is + * my (Mario) understanding on the issue... + * + * A function/program may have as its input variables a simple variable + * (BYTE, WORD, etc...), an array (ARRAY [1 .. 3] OF BYTE, ...) , or a structure. + * Nevertheless, when calling this function from within a st or il language statement + * it is not possible to allocate a value to a single element of the array or structure + * typed input variable, as the accepted syntax is simply '(' variable_name ':=' variable ')' + * Notice how the variable_name does not include things such as 'a.elem1' or 'a[1]'! + * + * Nevertheless, when configuring a program from within a configuration, + * it becomes possible to allocate values to individual elements of the + * array or structured type input variable, as the syntax is now + * '(' symbolic_variable ':=' data_sink|prog_data_source ')' + * Notice how the symbolic_variable _does_ include things such as 'a.elem1' or 'a[1]'! + * + * Conclusion: Unlike other locations in the syntax where SENDTO appears, + * here it is not valid to replace symbolic_variable with any_identifier! + * Nevertheless, it is also not correct to leave symbolic_variable as it is, + * as we have defined it to only include previously declared variables, + * which is not the case in this situation. Here symbolic_variable is refering + * to variables that were defined within the scope of the program that is being + * called, and _not_ within the scope of the configuration that is calling the + * program, so the variables in question are not declared in the current scope! + * + * We therefore need to define a new symbolic_variable, that accepts any_identifier + * instead of previosuly declared variable names, to be used in the definition of + * prog_cnxn! + */ +prog_cnxn: + any_symbolic_variable ASSIGN prog_data_source + {$$ = new prog_cnxn_assign_c($1, $3);} +| any_symbolic_variable SENDTO data_sink + {$$ = new prog_cnxn_sendto_c($1, $3);} +; + +prog_data_source: + constant +| enumerated_value +| global_var_reference +| direct_variable +; + +data_sink: + global_var_reference +| direct_variable +; + +instance_specific_initializations: + VAR_CONFIG instance_specific_init_list END_VAR + {$$ = new instance_specific_initializations_c($2);} +; + +// helper symbol for instance_specific_initializations // +instance_specific_init_list: + instance_specific_init ';' + {$$ = new instance_specific_init_list_c(); $$->add_element($1);} +| instance_specific_init_list instance_specific_init ';' + {$$ = $1; $$->add_element($2);} +; + + +instance_specific_init: +// +// resource_name '.' program_name '.' {fb_name '.'} +// ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' function_block_type_name ':=' structure_initialization)) +// +// prev_declared_resource_name '.' prev_declared_program_name '.' any_fb_name_list variable_name ':' located_var_spec_init +/* NOTE: variable_name has been changed to any_identifier (and not simply identifier) because the + * variables being referenced have been declared outside the scope currently being parsed! + */ +/* NOTE: program_name has not been changed to prev_declared_program_name because the + * programs being referenced have been declared outside the scope currently being parsed! + * The programs are only kept inside the scope of the resource in which they are defined. + */ + prev_declared_resource_name '.' program_name '.' any_fb_name_list any_identifier ':' located_var_spec_init + {$$ = new instance_specific_init_c($1, $3, $5, $6, NULL, $8);} +| prev_declared_resource_name '.' program_name '.' any_fb_name_list any_identifier location ':' located_var_spec_init + {$$ = new instance_specific_init_c($1, $3, $5, $6, $7, $9);} +| prev_declared_resource_name '.' program_name '.' any_fb_name_list any_identifier ':' fb_initialization + {$5->add_element($6); $$ = new instance_specific_init_c($1, $3, $5, NULL, NULL, $8);} +; + + +/* helper symbol for instance_specific_init */ +fb_initialization: + function_block_type_name ASSIGN structure_initialization + {$$ = new fb_initialization_c($1, $3);} +; + +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +/* helper symbol for many IL instructions, etc... */ +/* eat up any extra EOL tokens... */ + +eol_list: + EOL +| eol_list EOL +; + +/* +eol_list: + '\n' +| eol_list '\n' +; +*/ + +instruction_list: + il_instruction + {$$ = new instruction_list_c(); $$->add_element($1);} +| instruction_list il_instruction + {$$ = $1; $$->add_element($2);} +| instruction_list pragma + {$$ = $1; $$->add_element($2);} +| instruction_list error + {$$ = $1; + print_err_msg(current_filename, @2.last_line, "error in IL instruction."); + /* yychar */ + yyerrok; + } +; + + + +il_instruction: + il_incomplete_instruction eol_list + {$$ = new il_instruction_c(NULL, $1);} +| label ':' il_incomplete_instruction eol_list + {$$ = new il_instruction_c($1, $3);} +; + + +/* helper symbol for il_instruction */ +il_incomplete_instruction: + il_simple_operation +| il_expression +| il_jump_operation +| il_fb_call +| il_formal_funct_call +| il_return_operator +; + + +label: identifier; + + + +il_simple_operation: + il_simple_operator + {$$ = new il_simple_operation_c($1, NULL);} +| il_simple_operator_noclash il_operand + {$$ = new il_simple_operation_c($1, $2);} +| il_simple_operator_clash_il_operand + {$$ = new il_simple_operation_c($1.first, $1.second);} +/* NOTE: the line + * | il_simple_operator + * already contains the 'NOT' operator, as well as all the + * expression operators ('MOD', 'AND', etc...), all of which + * may also be a function name! This means that these operators/functions, + * without any operands, could be reduced to either an operator or a + * function call. I (Mario) have chosen to reduce it to an operator. + * + * The line + * | function_name + * has been replaced with the lines + * | function_name_no_clashes + * in order to include all possible function names except + * those whose names coincide with operators !! + */ +| function_name_no_clashes + {$$ = new il_function_call_c($1, NULL);} +/* NOTE: the line + * | il_simple_operator il_operand + * already contains the 'NOT', 'MOD', etc. operators, followed by a single il_operand, + * which may also be reduced to a function name with an operand_list of a single + * il_operand! This would lead us to a reduce/reduce conflict! + * + * I (Mario) have chosen to reduce it to an operand, rather than a function call. + * + * The line + * | function_name il_operand_list + * has been replaced with the line + * | function_name_no_clashes il_operand_list + * in order to include all possible function names except + * for the function names which clash with expression and simple operators. + * + * Note that: + * this alternative syntax does not cover the possibility of + * the function 'NOT', 'MOD', etc... being called with more than one il_operand! + * We therefore need to include an extra rule where the + * function_name_expression_clashes and function_name_simpleop_clashes + * are followed by a il_operand_list with __two__ or more il_operands!! + */ +| function_name_no_clashes il_operand_list + {$$ = new il_function_call_c($1, $2);} +| il_simple_operator_clash_il_operand ',' il_operand_list + {list_c *list = new il_operand_list_c(); + list->add_element($1.second); + FOR_EACH_ELEMENT(elem, $3, {list->add_element(elem);}) + $$ = new il_function_call_c(il_operator_c_2_identifier_c($1.first), list); + } +; + + + +il_simple_operator_clash_il_operand: + il_simple_operator_clash il_operand + {$$.first = $1; $$.second = $2;} +; + + + +il_expression: + il_expr_operator_noclash '(' eol_list ')' + {$$ = new il_expression_c($1, NULL, NULL);} +| il_expr_operator_noclash '(' il_operand eol_list ')' + {$$ = new il_expression_c($1, $3, NULL);} +| il_expr_operator_noclash '(' eol_list simple_instr_list ')' + {$$ = new il_expression_c($1, NULL, $4);} +| il_expr_operator_noclash '(' il_operand eol_list simple_instr_list ')' + {$$ = new il_expression_c($1, $3, $5);} +/* +*/ +| il_expr_operator_clash '(' eol_list ')' + {$$ = new il_expression_c($1, NULL, NULL);} +| il_expr_operator_clash '(' il_operand eol_list ')' + {$$ = new il_expression_c($1, $3, NULL);} +| il_expr_operator_clash '(' il_operand eol_list simple_instr_list ')' + {$$ = new il_expression_c($1, $3, $5);} +/* +*/ +| il_expr_operator_clash_eol_list simple_instr_list ')' + {$$ = new il_expression_c($1, NULL, $2);} +; + + +il_jump_operation: + il_jump_operator label + {$$ = new il_jump_operation_c($1, $2);} +; + + +il_fb_call: + il_call_operator prev_declared_fb_name + {$$ = new il_fb_call_c($1, $2, NULL, NULL);} +| il_call_operator prev_declared_fb_name '(' ')' + {$$ = new il_fb_call_c($1, $2, NULL, NULL);} +| il_call_operator prev_declared_fb_name '(' eol_list ')' + {$$ = new il_fb_call_c($1, $2, NULL, NULL);} +| il_call_operator prev_declared_fb_name '(' il_operand_list ')' + {$$ = new il_fb_call_c($1, $2, $4, NULL);} +| il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + {$$ = new il_fb_call_c($1, $2, NULL, $5);} +; + + +/* NOTE: Please read note above the definition of function_name_without_clashes */ +il_formal_funct_call: +/* function_name '(' eol_list ')' */ +/* NOTE: il_formal_funct_call is only used in the definition of + * - il_incomplete_instruction + * - il_simple_instruction + * In both of the above, il_expression also + * shows up as another option. This means that the functions whose + * names clash with expressions, followed by '(' eol_list ')', are + * already included. We must therefore leave them out in this + * definition in order to remove reduce/reduce conflicts. + * + * In summary: 'MOD' '(' eol_list ')', and all other functions whose + * names clash with expressions may be interpreted by the syntax by + * two different routes. I (Mario) chose to interpret them + * as operators, rather than as function calls! + */ + function_name_no_clashes '(' eol_list ')' + {$$ = new il_formal_funct_call_c($1, NULL);} +| function_name_simpleop_clashes '(' eol_list ')' + {$$ = new il_formal_funct_call_c($1, NULL);} +/* | function_name '(' eol_list il_param_list ')' */ +| function_name_no_clashes '(' eol_list il_param_list ')' + {$$ = new il_formal_funct_call_c($1, $4);} +| function_name_simpleop_clashes '(' eol_list il_param_list ')' + {$$ = new il_formal_funct_call_c($1, $4);} +/* the function_name_expression_clashes had to be first reduced to + * an intermediary symbol in order to remove a reduce/reduce conflict. + * In essence, the syntax requires more than one look ahead token + * in order to be parsed. We resolve this by reducing a collection of + * symbols into a temporary symbol (il_expr_operator_clash_eol_list), that + * will later be replaced by the correct symbol. The correct symbol will + * now be determined by a single look ahead token, as all the common + * symbols have been reduced to the temporary symbol + * il_expr_operator_clash_eol_list ! + * + * Unfortunately, this work around results in the wrong symbol + * being created for the abstract syntax tree. + * We need to figure out which symbol was created, destroy it, + * and create the correct symbol for our case. + * This is a lot of work, so I put it in a function + * at the end of this file... il_operator_c_2_identifier_c() + */ +| il_expr_operator_clash_eol_list il_param_list ')' + {$$ = new il_formal_funct_call_c(il_operator_c_2_identifier_c($1), $2);} +; + + +il_expr_operator_clash_eol_list: + il_expr_operator_clash '(' eol_list + {$$ = $1;} +; + + +il_operand: + variable +| enumerated_value +| constant +; + + +il_operand_list: + il_operand + {$$ = new il_operand_list_c(); $$->add_element($1);} +| il_operand_list ',' il_operand + {$$ = $1; $$->add_element($3);} +; + + +simple_instr_list: + il_simple_instruction + {$$ = new simple_instr_list_c(); $$->add_element($1);} +| simple_instr_list il_simple_instruction + {$$ = $1; $$->add_element($2);} +; + + +il_simple_instruction: + il_simple_operation eol_list +| il_expression eol_list +| il_formal_funct_call eol_list +; + + +/* NOTE: the correct definition of il_param_list is + * il_param_list ::= {il_param_instruction} il_param_last_instruction + * + * where {...} denotes zero or many il_param_instruction's. + * + * We could do this by defining the following: + * il_param_list: il_param_instruction_list il_param_last_instruction; + * il_param_instruction_list : ** empty ** | il_param_instruction_list il_param_instruction; + * + * Unfortunately, the above leads to reduce/reduce conflicts. + * The chosen alternative (as follows) does not have any conflicts! + * il_param_list: il_param_last_instruction | il_param_instruction_list il_param_last_instruction; + * il_param_instruction_list : il_param_instruction_list | il_param_instruction_list il_param_instruction; + */ +il_param_list: + il_param_instruction_list il_param_last_instruction + {$$ = $1; $$->add_element($2);} +| il_param_last_instruction + {$$ = new il_param_list_c(); $$->add_element($1);} +; + + +/* Helper symbol for il_param_list */ +il_param_instruction_list: + il_param_instruction + {$$ = new il_param_list_c(); $$->add_element($1);} +| il_param_instruction_list il_param_instruction + {$$ = $1; $$->add_element($2);} +; + + +il_param_instruction: + il_param_assignment ',' eol_list +| il_param_out_assignment ',' eol_list +; + + +il_param_last_instruction: + il_param_assignment eol_list +| il_param_out_assignment eol_list +; + + +il_param_assignment: + il_assign_operator il_operand + {$$ = new il_param_assignment_c($1, $2, NULL);} +| il_assign_operator '(' eol_list simple_instr_list ')' + {$$ = new il_param_assignment_c($1, NULL, $4);} +; + + +il_param_out_assignment: + il_assign_out_operator variable + {$$ = new il_param_out_assignment_c($1, $2);} +; + + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +sendto_identifier: sendto_identifier_token {$$ = new identifier_c($1);}; + + +/* NOTE: + * The spec includes the operator 'EQ ' + * Note that EQ is followed by a space. + * I am considering this a typo, and defining the operator + * as 'EQ' + * (Mario) + */ +LD_operator: LD {$$ = new LD_operator_c();}; +LDN_operator: LDN {$$ = new LDN_operator_c();}; +ST_operator: ST {$$ = new ST_operator_c();}; +STN_operator: STN {$$ = new STN_operator_c();}; +NOT_operator: NOT {$$ = new NOT_operator_c();}; +S_operator: S {$$ = new S_operator_c();}; +R_operator: R {$$ = new R_operator_c();}; +S1_operator: S1 {$$ = new S1_operator_c();}; +R1_operator: R1 {$$ = new R1_operator_c();}; +CLK_operator: CLK {$$ = new CLK_operator_c();}; +CU_operator: CU {$$ = new CU_operator_c();}; +CD_operator: CD {$$ = new CD_operator_c();}; +PV_operator: PV {$$ = new PV_operator_c();}; +IN_operator: IN {$$ = new IN_operator_c();}; +PT_operator: PT {$$ = new PT_operator_c();}; +AND_operator: AND {$$ = new AND_operator_c();}; +AND2_operator: AND2 {$$ = new AND_operator_c();}; /* '&' in the source code! */ +OR_operator: OR {$$ = new OR_operator_c();}; +XOR_operator: XOR {$$ = new XOR_operator_c();}; +ANDN_operator: ANDN {$$ = new ANDN_operator_c();}; +ANDN2_operator: ANDN2 {$$ = new ANDN_operator_c();}; /* '&N' in the source code! */ +ORN_operator: ORN {$$ = new ORN_operator_c();}; +XORN_operator: XORN {$$ = new XORN_operator_c();}; +ADD_operator: ADD {$$ = new ADD_operator_c();}; +SUB_operator: SUB {$$ = new SUB_operator_c();}; +MUL_operator: MUL {$$ = new MUL_operator_c();}; +DIV_operator: DIV {$$ = new DIV_operator_c();}; +MOD_operator: MOD {$$ = new MOD_operator_c();}; +GT_operator: GT {$$ = new GT_operator_c();}; +GE_operator: GE {$$ = new GE_operator_c();}; +EQ_operator: EQ {$$ = new EQ_operator_c();}; +LT_operator: LT {$$ = new LT_operator_c();}; +LE_operator: LE {$$ = new LE_operator_c();}; +NE_operator: NE {$$ = new NE_operator_c();}; +CAL_operator: CAL {$$ = new CAL_operator_c();}; +CALC_operator: CALC {$$ = new CALC_operator_c();}; +CALCN_operator: CALCN {$$ = new CALCN_operator_c();}; +RET_operator: RET {$$ = new RET_operator_c();}; +RETC_operator: RETC {$$ = new RETC_operator_c();}; +RETCN_operator: RETCN {$$ = new RETCN_operator_c();}; +JMP_operator: JMP {$$ = new JMP_operator_c();}; +JMPC_operator: JMPC {$$ = new JMPC_operator_c();}; +JMPCN_operator: JMPCN {$$ = new JMPCN_operator_c();}; + +/* + MAY CONFLICT WITH STANDARD FUNCTION NAMES!!! + +NOT_operator: NOT {new NOT_operator_c();}; + +AND_operator: AND {new AND_operator_c();}; +OR_operator: OR {new OR_operator_c();}; +XOR_operator: XOR {new XOR_operator_c();}; + +ADD_operator: ADD {new ADD_operator_c();}; +SUB_operator: SUB {new SUB_operator_c();}; +MUL_operator: MUL {new MUL_operator_c();}; +DIV_operator: DIV {new DIV_operator_c();}; +MOD_operator: MOD {new MOD_operator_c();}; + +GT_operator: GT {new GT_operator_c();}; +GE_operator: GE {new GE_operator_c();}; +EQ_operator: EQ {new EQ_operator_c();}; +LT_operator: LT {new LT_operator_c();}; +LE_operator: LE {new LE_operator_c();}; +NE_operator: NE {new NE_operator_c();}; +*/ + + +il_simple_operator: + il_simple_operator_clash +| il_simple_operator_noclash +; + + +il_simple_operator_noclash: + LD_operator +| LDN_operator +| ST_operator +| STN_operator +| S_operator +| R_operator +| S1_operator +| R1_operator +| CLK_operator +| CU_operator +| CD_operator +| PV_operator +| IN_operator +| PT_operator +| il_expr_operator_noclash +; + + +il_simple_operator_clash: + il_simple_operator_clash1 +| il_simple_operator_clash2 +; + +il_simple_operator_clash1: + NOT_operator +; + +il_simple_operator_clash2: + il_expr_operator_clash +; + + +/* +il_expr_operator: + il_expr_operator_noclash +| il_expr_operator_clash +; +*/ + +il_expr_operator_clash: + AND_operator +| OR_operator +| XOR_operator +| ADD_operator +| SUB_operator +| MUL_operator +| DIV_operator +| MOD_operator +| GT_operator +| GE_operator +| EQ_operator +| LT_operator +| LE_operator +| NE_operator +; + + +il_expr_operator_noclash: + ANDN_operator +| ANDN2_operator /* string '&N' in source code! */ +| AND2_operator /* string '&' in source code! */ +| ORN_operator +| XORN_operator +; + + + + +il_assign_operator: +/* variable_name ASSIGN */ + any_identifier ASSIGN +; + + +il_assign_out_operator: +/* variable_name SENDTO */ +/* any_identifier SENDTO */ + sendto_identifier SENDTO + {$$ = new il_assign_out_operator_c(NULL, $1);} +/*| NOT variable_name SENDTO */ +| NOT sendto_identifier SENDTO + {$$ = new il_assign_out_operator_c(new not_paramassign_c(), $2);} +; + + +il_call_operator: + CAL_operator +| CALC_operator +| CALCN_operator +; + + +il_return_operator: + RET_operator +| RETC_operator +| RETCN_operator +; + + +il_jump_operator: + JMP_operator +| JMPC_operator +| JMPCN_operator +; + + +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +expression: + xor_expression +| expression OR xor_expression + {$$ = new or_expression_c($1, $3);} +; + +xor_expression: + and_expression +| xor_expression XOR and_expression + {$$ = new xor_expression_c($1, $3);} +; + +and_expression: + comparison +| and_expression '&' comparison + {$$ = new and_expression_c($1, $3);} +| and_expression AND comparison + {$$ = new and_expression_c($1, $3);} +/* NOTE: The lexical parser never returns the token '&'. + * The '&' string is interpreted by the lexcial parser as the token + * AND2! + * This means that the first rule with '&' is actually not required, + * but we leave it in nevertheless just in case we later decide + * to remove theh AND2 token... + */ +| and_expression AND2 comparison + {$$ = new and_expression_c($1, $3);} +; + +comparison: + equ_expression +| comparison '=' equ_expression + {$$ = new equ_expression_c($1, $3);} +| comparison OPER_NE equ_expression + {$$ = new notequ_expression_c($1, $3);} +; + +equ_expression: + add_expression +| equ_expression '<' add_expression + {$$ = new lt_expression_c($1, $3);} +| equ_expression '>' add_expression + {$$ = new gt_expression_c($1, $3);} +| equ_expression OPER_LE add_expression + {$$ = new le_expression_c($1, $3);} +| equ_expression OPER_GE add_expression + {$$ = new ge_expression_c($1, $3);} +; + +/* Not required... +comparison_operator: '<' | '>' | '>=' '<=' +*/ + +add_expression: + term +| add_expression '+' term + {$$ = new add_expression_c($1, $3);} +| add_expression '-' term + {$$ = new sub_expression_c($1, $3);} +; + +/* Not required... +add_operator: '+' | '-' +*/ + +term: + power_expression +| term '*' power_expression + {$$ = new mul_expression_c($1, $3);} +| term '/' power_expression + {$$ = new div_expression_c($1, $3);} +| term MOD power_expression + {$$ = new mod_expression_c($1, $3);} +; + +/* Not required... +multiply_operator: '*' | '/' | 'MOD' +*/ + +power_expression: + unary_expression +| power_expression OPER_EXP unary_expression + {$$ = new power_expression_c($1, $3);} +; + + +unary_expression: + primary_expression +| '-' primary_expression + {$$ = new neg_expression_c($2);} +| NOT primary_expression + {$$ = new not_expression_c($2);} +; + +/* Not required... +unary_operator: '-' | 'NOT' +*/ + + +/* NOTE: using constant as a possible symbol for primary_expression + * leads to a reduce/reduce conflict. + * + * The text '-9' may be parsed as either a + * expression<-primary_expression<-constant<-signed_integer + * (i.e. the constant 9 negative) + * OR + * expression<-unary_expression<-constant<-integer + * (i.e. the constant 9, preceded by a unary negation) + * + * To remove the conlfict, we only allow constants without + * a preceding '-' to be used in primary_expression + */ +primary_expression: +/* constant */ + non_negative_constant +| enumerated_value +| variable +| '(' expression ')' + {$$ = $2;} +| function_invocation +; + + +/* intermediate helper symbol for primary_expression */ +/* NOTE: function_name includes the standard function name 'NOT' ! + * This introduces a reduce/reduce conflict, as NOT(var) + * may be parsed as either a function_invocation, or a + * unary_expression. + * + * I (Mario) have opted to remove the possible reduction + * to function invocation, which means replacing the rule + * function_name '(' param_assignment_list ')' + * with + * function_name_no_NOT_clashes '(' param_assignment_list ')' + * + * Notice how the new rule does not include the situation where + * the function NOT is called with more than one parameter, which + * the original rule does include! Callinf the NOT function with more + * than one argument is probably a semantic error anyway, so it + * doesn't make much sense to take it into account. + * + * Nevertheless, if we were to to it entirely correctly, + * leaving the semantic checks for the next compiler stage, + * this syntax parser would need to include such a possibility. + * + * We will leave this out for now. No need to complicate the syntax + * more than the specification does by contradicting itself, and + * letting names clash! + */ +function_invocation: +/* function_name '(' param_assignment_list ')' */ + function_name_no_NOT_clashes '(' param_assignment_list ')' + {$$ = new function_invocation_c($1, $3);} +; + + +/********************/ +/* B 3.2 Statements */ +/********************/ +statement_list: + /* empty */ + {$$ = new statement_list_c();} +| statement_list statement ';' + {$$ = $1; $$->add_element($2);} +| statement_list pragma + {$$ = $1; $$->add_element($2);} +| statement_list error ';' + {$$ = $1; + print_err_msg(current_filename, @2.last_line, "error in statement."); + /* yychar */ + yyerrok; + } +; + + +statement: + assignment_statement +| subprogram_control_statement +| selection_statement +| iteration_statement +; + + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +assignment_statement: + variable ASSIGN expression + {$$ = new assignment_statement_c($1, $3);} +; + + + + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +subprogram_control_statement: + fb_invocation +| return_statement +; + + +return_statement: + RETURN {$$ = new return_statement_c();} +; + + + +fb_invocation: + prev_declared_fb_name '(' ')' + {$$ = new fb_invocation_c($1, NULL); } +| prev_declared_fb_name '(' param_assignment_list ')' + {$$ = new fb_invocation_c($1, $3);} +; + + +/* helper symbol for + * - fb_invocation + * - function_invocation + */ +param_assignment_list: + param_assignment + {$$ = new param_assignment_list_c(); $$->add_element($1);} +| param_assignment_list ',' param_assignment + {$$ = $1; $$->add_element($3);} +; + + + +param_assignment: +/* variable_name ASSIGN expression */ + any_identifier ASSIGN expression + {$$ = new input_variable_param_assignment_c($1, $3);} +| expression +/*| variable_name SENDTO variable */ +/*| any_identifier SENDTO variable */ +| sendto_identifier SENDTO variable + {$$ = new output_variable_param_assignment_c(NULL,$1, $3);} +/*| variable_name SENDTO variable */ +/*| NOT any_identifier SENDTO variable*/ +| NOT sendto_identifier SENDTO variable + {$$ = new output_variable_param_assignment_c(new not_paramassign_c(),$2, $4);} +; + + + + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ +selection_statement: + if_statement +| case_statement +; + + +if_statement: + IF expression THEN statement_list elseif_statement_list END_IF + {$$ = new if_statement_c($2, $4, $5, NULL);} +| IF expression THEN statement_list elseif_statement_list ELSE statement_list END_IF + {$$ = new if_statement_c($2, $4, $5, $7);} +; + +/* helper symbol for if_statement */ +elseif_statement_list: + /* empty */ + {$$ = new elseif_statement_list_c();} +| elseif_statement_list elseif_statement + {$$ = $1; $$->add_element($2);} +; + +/* helper symbol for elseif_statement_list */ +elseif_statement: + ELSIF expression THEN statement_list + {$$ = new elseif_statement_c($2, $4);} +; + + +case_statement: + CASE expression OF case_element_list END_CASE + {$$ = new case_statement_c($2, $4, NULL);} +| CASE expression OF case_element_list ELSE statement_list END_CASE + {$$ = new case_statement_c($2, $4, $6);} +; + + +/* helper symbol for case_statement */ +case_element_list: + case_element + {$$ = new case_element_list_c(); $$->add_element($1);} +| case_element_list case_element + {$$ = $1; $$->add_element($2);} +; + + +case_element: + case_list ':' statement_list + {$$ = new case_element_c($1, $3);} +; + + +case_list: + case_list_element + {$$ = new case_list_c(); $$->add_element($1);} +| case_list ',' case_list_element + {$$ = $1; $$->add_element($3);} +; + + +case_list_element: + signed_integer +| enumerated_value +| subrange +; + + + + + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ +iteration_statement: + for_statement +| while_statement +| repeat_statement +| exit_statement +; + + +for_statement: + FOR control_variable ASSIGN expression TO expression BY expression DO statement_list END_FOR + {$$ = new for_statement_c($2, $4, $6, $8, $10);} +| FOR control_variable ASSIGN expression TO expression DO statement_list END_FOR + {$$ = new for_statement_c($2, $4, $6, NULL, $8);} +; + +/* The spec has the syntax + * control_variable: identifier; + * but then defines the semantics of control_variable + * (Section 3.3.2.4) as being of an integer type + * (e.g., SINT, INT, or DINT). + * + * Obviously this presuposes that the control_variable + * must have been declared in some VAR .. END_VAR + * construct, so I (Mario) changed the syntax to read + * control_variable: prev_declared_variable_name; +*/ +control_variable: prev_declared_variable_name {$$ = $1;}; + +/* Integrated directly into for_statement */ +/* +for_list: + expression TO expression [BY expression] +; +*/ + + +while_statement: + WHILE expression DO statement_list END_WHILE + {$$ = new while_statement_c($2, $4);} +; + + +repeat_statement: + REPEAT statement_list UNTIL expression END_REPEAT + {$$ = new repeat_statement_c($2, $4);} +; + + +exit_statement: + EXIT {$$ = new exit_statement_c();} +; + + + + + +%% + +#include /* required for printf() */ +#include +#include "../util/symtable.hh" + +/* variables defined in code generated by flex... */ +extern FILE *yyin; +extern int yylineno; + +/* The following function is called automatically by bison whenever it comes across + * an error. Unfortunately it calls this function before executing the code that handles + * the error itself, so we cannot print out the correct line numbers of the error location + * over here. + * Our solution is to store the current error message in a global variable, and have all + * error action handlers call the function print_err_msg() after setting the location + * (line number) variable correctly. + */ +const char *current_error_msg; +void yyerror (const char *error_msg) { + current_error_msg = error_msg; +/* fprintf(stderr, "error %d: %s\n", yynerrs // global variable //, error_msg); */ + print_include_stack(); +} + + +void print_err_msg(const char *filename, int lineno, const char *additional_error_msg) { + fprintf(stderr, "error %d: %s\n", yynerrs, additional_error_msg); + print_include_stack(); + fprintf(stderr, "%s:%d: %s\n", filename, lineno, current_error_msg); +} + +/* Function only called from within flex! + * + * search for a symbol in either of the two symbol tables + * declared above, and return the token id of the first + * symbol found. + * Searches first in the variables, and only if not found + * does it continue searching in the library elements + */ +int get_identifier_token(const char *identifier_str) { +// std::cout << "get_identifier_token(" << identifier_str << "): \n"; + int token_id; + + if ((token_id = variable_name_symtable.find_value(identifier_str)) == variable_name_symtable.end_value()) + if ((token_id = library_element_symtable.find_value(identifier_str)) == library_element_symtable.end_value()) + return identifier_token; + return token_id; +} + + + +/* convert between an il_operator to a function name */ +/* This a kludge! + * It is required because our language requires more than one + * look ahead token, and bison only works with one! + */ +#define op_2_str(op, str) {\ + op ## _operator_c *ptr = dynamic_cast(il_operator); \ + if (ptr != NULL) name = str; \ +} + +/* NOTE: this code is very ugly and un-eficient, but I (Mario) have many + * more things to worry about right now, so just let it be... + */ +symbol_c *il_operator_c_2_identifier_c(symbol_c *il_operator) { + const char *name = NULL; + + op_2_str(NOT, "NOT"); + + op_2_str(AND, "AND"); + op_2_str(OR, "OR"); + op_2_str(XOR, "XOR"); + op_2_str(ADD, "ADD"); + op_2_str(SUB, "SUB"); + op_2_str(MUL, "MUL"); + op_2_str(DIV, "DIV"); + op_2_str(MOD, "MOD"); + op_2_str(GT, "GT"); + op_2_str(GE, "GE"); + op_2_str(EQ, "EQ"); + op_2_str(LT, "LT"); + op_2_str(LE, "LE"); + op_2_str(NE, "NE"); + + op_2_str(LD, "LD"); + op_2_str(LDN, "LDN"); + op_2_str(ST, "ST"); + op_2_str(STN, "STN"); + + op_2_str(S, "S"); + op_2_str(R, "R"); + op_2_str(S1, "S1"); + op_2_str(R1, "R1"); + + op_2_str(CLK, "CLK"); + op_2_str(CU, "CU"); + op_2_str(CD, "CD"); + op_2_str(PV, "PV"); + op_2_str(IN, "IN"); + op_2_str(PT, "PT"); + + op_2_str(ANDN, "ANDN"); + op_2_str(ORN, "ORN"); + op_2_str(XORN, "XORN"); + + op_2_str(ADD, "ADD"); + op_2_str(SUB, "SUB"); + op_2_str(MUL, "MUL"); + op_2_str(DIV, "DIV"); + + op_2_str(GT, "GT"); + op_2_str(GE, "GE"); + op_2_str(EQ, "EQ"); + op_2_str(LT, "LT"); + op_2_str(LE, "LE"); + op_2_str(NE, "NE"); + + op_2_str(CAL, "CAL"); + op_2_str(CALC, "CALC"); + op_2_str(CALCN, "CALCN"); + op_2_str(RET, "RET"); + op_2_str(RETC, "RETC"); + op_2_str(RETCN, "RETCN"); + op_2_str(JMP, "JMP"); + op_2_str(JMPC, "JMPC"); + op_2_str(JMPCN, "JMPCN"); + + if (name == NULL) + ERROR; + + free(il_operator); + return new identifier_c(strdup(name)); +} + + + + +/* + * Join two strings together. Allocate space with malloc(3). + */ +static char *strdup2(const char *a, const char *b) { + char *res = (char *)malloc(strlen(a) + strlen(b) + 1); + + if (!res) + return NULL; + return strcat(strcpy(res, a), b); /* safe, actually */ +} + +/* + * Join three strings together. Allocate space with malloc(3). + */ +static char *strdup3(const char *a, const char *b, const char *c) { + char *res = (char *)malloc(strlen(a) + strlen(b) + strlen(c) + 1); + + if (!res) + return NULL; + return strcat(strcat(strcpy(res, a), b), c); /* safe, actually */ +} + + + + +const char *standard_function_names[] = { +// 2.5.1.5.1 Type conversion functions +/* + *_TO_** + TRUNC + *_BCD_TO_** + **_TO_BCD_* + (REAL or LREAL to SINT, INT, DINT or LINT) +*/ +"TRUNC", +"TIME_TO_REAL", +// 2.5.1.5.2 Numerical functions +// Table 23 - Standard functions of one numeric variable +"ABS","SQRT","LN","LOG","EXP","SIN","COS","TAN","ASIN","ACOS","ATAN", +// Table 24 - Standard arithmetic functions +"ADD","MUL","SUB","DIV","MOD"/* See note (a) */,"EXPT","MOVE", +// 2.5.1.5.3 Bit string functions +// Table 25 - Standard bit shift functions +"SHL","SHR","ROR","ROL", +// 2.5.1.5.4 Selection and comparison functions +// Table 26 - Standard bitwise Boolean functions +"AND","OR","XOR","NOT", +// Table 27 - Standard selection functions +"SEL","MAX","MIN","LIMIT","MUX", +// Table 28 - Standard comparison functions +"GT","GE","EQ","LE","LT","NE", +// 2.5.1.5.5 Character string functions +// Table 29 - Standard character string functions +"LEN","LEFT","RIGHT","MID","CONCAT","INSERT","DELETE","REPLACE","FIND", +// 2.5.1.5.6 Functions of time data types +// Table 30 - Functions of time data types +"ADD_TIME","ADD_TOD_TIME","ADD_DT_TIME","SUB_TIME","SUB_DATE_DATE", +"SUB_TOD_TIME","SUB_TOD_TOD","SUB_DT_TIME","SUB_DT_DT","MULTIME", +"DIVTIME","CONCAT_DATE_TOD", +// 2.5.1.5.7 Functions of enumerated data types +// Table 31 - Functions of enumerated data types +// "SEL", /* already above! We cannot have duplicates! */ +// "MUX", /* already above! We cannot have duplicates! */ +// "EQ", /* already above! We cannot have duplicates! */ +// "NE", /* already above! We cannot have duplicates! */ + +/* end of array marker! Do not remove! */ +NULL + +/* Note (a): + * This function has a name equal to a reserved keyword. + * This means that adding it here is irrelevant because the + * lexical parser will consider it the XXX token before + * it interprets it as an identifier and looks it up + * in the library elements symbol table. + */ +}; + + + + +const char *standard_function_block_names[] = { +// 2.5.2.3.1 Bistable elements +// Table 34 - Standard bistable function blocks +//"SR","RS", +// 2.5.2.3.2 Edge detection +// Table 35 - Standard edge detection function blocks +"R_TRIG","F_TRIG", +// 2.5.2.3.3 Counters +// Table 36 - Standard counter function blocks +"CTU","CTU_LINT","CTU_UDINT","CTU_ULINT", +"CTD","CTD_DINT","CTD_LINT","CTD_UDINT", +"CTUD","CTUD_DINT","CTUD_LINT","CTUD_ULINT", +// 2.5.2.3.4 Timers +// Table 37 - Standard timer function blocks +"TP","TON","TOF", +/* end of array marker! Do not remove! */ +NULL +}; + + +#define LIBFILE "ieclib.txt" +#define DEF_LIBFILENAME LIBDIRECTORY "/" LIBFILE + +int stage1_2(const char *filename, const char *includedir, symbol_c **tree_root_ref) { + FILE *in_file = NULL, *lib_file = NULL; + char *libfilename = NULL; + + if((in_file = fopen(filename, "r")) == NULL) { + char *errmsg = strdup2("Error opening main file ", filename); + perror(errmsg); + free(errmsg); + return -1; + } + + if (includedir != NULL) { + if ((libfilename = strdup3(includedir, "/", LIBFILE)) == NULL) { + fprintf (stderr, "Out of memory. Bailing out!\n"); + return -1; + } + + if((lib_file = fopen(libfilename, "r")) == NULL) { + char *errmsg = strdup2("Error opening library file ", libfilename); + perror(errmsg); + free(errmsg); + } + } + + if (lib_file == NULL) { + /* we try again... */ + if ((libfilename = strdup(DEF_LIBFILENAME)) == NULL) { + fprintf (stderr, "Out of memory. Bailing out!\n"); + return -1; + } + + if((lib_file = fopen(libfilename, "r")) == NULL) { + char *errmsg = strdup2("Error opening library file ", libfilename); + perror(errmsg); + free(errmsg); + } + } + + if (lib_file == NULL) { + /* we give up... */ + free(libfilename); + fclose(in_file); + return -1; + } + + /* first parse the standard library file... */ + yyin = lib_file; + yylineno = 1; + allow_function_overloading = true; + current_filename = libfilename; + if (yyparse() != 0) + ERROR; + + if (yynerrs > 0) { + fprintf (stderr, "\nFound %d error(s) in %s. Bailing out!\n", yynerrs /* global variable */, libfilename); + ERROR; + } + free(libfilename); + fclose(lib_file); + + /* if by any chance the library is not complete, we + * now add the missing reserved keywords to the list!!! + */ + for(int i = 0; standard_function_names[i] != NULL; i++) + if (library_element_symtable.find_value(standard_function_names[i]) == + library_element_symtable.end_value()) + library_element_symtable.insert(standard_function_names[i], standard_function_name_token); + + for(int i = 0; standard_function_block_names[i] != NULL; i++) + if (library_element_symtable.find_value(standard_function_block_names[i]) == + library_element_symtable.end_value()) + library_element_symtable.insert(standard_function_block_names[i], standard_function_block_name_token); + +#if YYDEBUG + yydebug = 1; +#endif + + /* now parse the input file... */ + yyin = in_file; + yylineno = 1; + allow_function_overloading = false; + current_filename = filename; + if (yyparse() != 0) + exit(EXIT_FAILURE); + + if (yynerrs > 0) { + fprintf (stderr, "\nFound %d error(s). Bailing out!\n", yynerrs /* global variable */); + exit(EXIT_FAILURE); + } + + if (tree_root_ref != NULL) + *tree_root_ref = tree_root; + + fclose(in_file); + return 0; +} + + + diff -r 000000000000 -r fb772792efd1 stage4/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/Makefile Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,30 @@ +# include the system specific Makefile +#include ../../../Makefile.$(shell uname) + + + + +default: all + +all: stage4.o + +clean: + rm -f *.o + find . -depth -mindepth 2 -maxdepth 2 -name Makefile -printf %h\\n | xargs -i make -C{} $@ + + +#get warnings, debugging information and optimization +CXXFLAGS = -Wall -pedantic -Wpointer-arith -Wwrite-strings +# CXXFLAGS += -Werror + +CXXFLAGS += -ggdb -O3 -funroll-loops +# Note: if the optimizer crashes, we'll leave out the -O3 for those files + +CXXFLAGS += -I. -I../* -I../../absyntax + + + +#how to make things in subdirectories etc +../% /% absyntax/% stage1_2/% stage3/% stage4/% util/%: + $(MAKE) -C $(@D) $(@F) + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/Makefile Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,39 @@ +# include the system specific Makefile +#include ../../../../Makefile.$(shell uname) + + + + + +default: all + +all: generate_cc.o + +clean: + -rm -f *.o */*.o Makefile.depend + +#get warnings, debugging information and optimization +CXXFLAGS = -Wall -pedantic -Wpointer-arith -Wwrite-strings +# CXXFLAGS += -Werror + +CXXFLAGS += -ggdb -O3 -funroll-loops +# Note: if the optimizer crashes, we'll leave out the -O3 for those files + +CXXFLAGS += -I. -I../* -I../../absyntax + + + +#how to make things from other directories if they are missing +../% /%: + $(MAKE) -C $(@D) $(@F) + + +Makefile.depend depend: + $(CXX) -MM -MG -I. *.cc \ + | perl -pe 's/:/ Makefile.depend:/' > Makefile.depend + +include Makefile.depend + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/decompose_var_instance_name.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/decompose_var_instance_name.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,132 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* Decomposes a variable instance name into its constituents, + * example: + * window.points[1].coordinate.x + * + * will succesfully return + * - window + * - points + * - coordinate + * - x + * on succesive calls to decompose_var_instance_name_c::next_part() + */ +class decompose_var_instance_name_c: null_visitor_c { + + private: + symbol_c *variable_name; + symbol_c *next_variable_name; + symbol_c *current_recursive_variable_name; + symbol_c *previously_returned_variable_name; + + public: + decompose_var_instance_name_c(symbol_c *variable_instance_name) { + variable_name = variable_instance_name; + next_variable_name = NULL; + current_recursive_variable_name = NULL; + previously_returned_variable_name = NULL; + } + + public: + symbol_c *next_part(void) { + /* We must always start from the top! + * See note in the structured_variable_c visitor + * to understand why... + */ + symbol_c *res = (symbol_c *)variable_name->accept(*this); + next_variable_name = current_recursive_variable_name; + + if (previously_returned_variable_name == res) + return NULL; + previously_returned_variable_name = res; + return res; + } + + public: +/*********************/ +/* B 1.4 - Variables */ +/*********************/ + void *visit(symbolic_variable_c *symbol) {return (void *)(symbol->var_name);} + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ + void *visit(direct_variable_c *symbol) {return (void *)symbol;} + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +// SYM_REF2(array_variable_c, subscripted_variable, subscript_list) + void *visit(array_variable_c *symbol) { + /* NOTE: the subscripted_variable may itself be a structure!, + * so we must recursevily visit! + */ + return symbol->subscripted_variable->accept(*this); + } + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a tructured variable! + * Code handling a structured_variable_c must take + * this into account! + */ +//SYM_REF2(structured_variable_c, record_variable, field_selector) + void *visit(structured_variable_c *symbol) { + /* NOTE: The following code will not work, as structured_variable_c + * are grouped on the left, and not on the right! + * + * example: window.origin.x + * will result in + * s1 = structured_variable_c("window, "origin"); + * s2 = structured_variable_c(s1, "x"); + * AND NOT + * s1 = structured_variable_c("origin", "x"); + * s2 = structured_variable_c("window", s1); + * + * as the following code assumes!! + * + current_variable_name = symbol->field_selector; + return symbol->record_variable->accept(*this); + */ + + /* The correct code, is therefore more complex... */ + if (next_variable_name == symbol) { + /* NOTE: field_selector is always an identifier_c, + * so we do not have to recursevily visit it again... + * return (void *)symbol->field_selector->accept(*this); -> NOT REQUIRED!! + */ + return (void *)symbol->field_selector; + } + + current_recursive_variable_name = symbol; + return symbol->record_variable->accept(*this); + } +}; + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/function_call_iterator.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/function_call_iterator.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,150 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Function call parameter iterator. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + +//#include "generate_cc.hh" + + + + +/* given a function_body_c, iterate through each + * function in/out/inout parameter, returning the name + * of each parameter... + */ +class function_call_iterator_c : public iterator_visitor_c { + private: + symbol_c *start_symbol; + int next_fcall, fcall_count; + //identifier_c *current_fcall_name; + symbol_c *current_fcall_name; + //function_invocation_c *current_finvocation; + symbol_c *current_finvocation; + + public: + /* initialise the iterator object. + * We must be given a reference to the function declaration + * that will be analysed... + */ + function_call_iterator_c(symbol_c *symbol) { + this->start_symbol = symbol; + next_fcall = fcall_count = 0; + current_finvocation = NULL; + current_fcall_name = NULL; + } + + /* Skip to the next function call. After object creation, + * the object references _before_ the first, so + * this function must be called once to get the object to + * reference the first function call... + * + * Returns the function_invocation_c! + */ + //function_invocation_c *next(void) {TRACE("function_call_iterator_c::next(): called "); + symbol_c *next(void) {TRACE("function_call_iterator_c::next(): called "); + fcall_count = 0; + next_fcall++; + current_finvocation = NULL; + current_fcall_name = NULL; + + start_symbol->accept(*this); + return current_finvocation; + } + + /* Returns the name of the currently referenced function invocation */ + identifier_c *fname(void) { + identifier_c *identifier = dynamic_cast(current_fcall_name); + if (identifier == NULL) ERROR; + return identifier; + } + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ + void *visit(function_invocation_c *symbol) { + fcall_count++; + if (next_fcall == fcall_count) { + current_finvocation = symbol; + current_fcall_name = symbol->function_name; + } + return NULL; + } + + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +/* | function_name [il_operand_list] */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list) + void *visit(il_function_call_c *symbol) { + fcall_count++; + if (next_fcall == fcall_count) { + current_finvocation = symbol; + current_fcall_name = symbol->function_name; + } + return NULL; + } + + + +/* | function_name '(' eol_list [il_param_list] ')' */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list) + void *visit(il_formal_funct_call_c *symbol) { + fcall_count++; + if (next_fcall == fcall_count) { + current_finvocation = symbol; + current_fcall_name = symbol->function_name; + } + return NULL; + } + + +}; + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/function_call_param_iterator.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/function_call_param_iterator.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,540 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Function call parameter iterator. + * It will iterate through the formal parameters of a function call + * (i.e. function calls using the foo(, , ...) syntax). + * and/or search through the non-formal parameters of a function call + * (i.e. function calls using the foo( = , = , ...) syntax). + * + * Calls to function blocks and programs are also supported. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + +//#include "generate_cc.hh" + + +#include "../../absyntax/visitor.hh" + + +/* + * Function call parameter iterator. + * It will iterate through the formal parameters of a function call + * (i.e. function calls using the foo(, , ...) syntax). + * and/or search through the non-formal parameters of a function call + * (i.e. function calls using the foo( = , = , ...) syntax). + * + * Calls to function blocks and programs are also supported. + * + * Note that calls to next() will only iterate through formal parameters, + * and calls to search() will only serach through non-formal parameters. + */ + +class function_call_param_iterator_c : public null_visitor_c { + private: + /* a pointer to the function call + * (or function block or program call!) + */ + symbol_c *f_call; + int next_param, param_count; + identifier_c *search_param_name; + + /* Which operation of the class was called... + * Search a parameter, or iterate to the next parameter. + */ + typedef enum {iterate_op, search_op} operation_t; + operation_t current_operation; + + private: + void *search_list(list_c *list) { + switch (current_operation) { + case iterate_op: + for(int i = 0; i < list->n; i++) { + void *res = list->elements[i]->accept(*this); + if (NULL != res) { + /* It went through the handle_parameter_assignment() function, + * and is therefore a parameter assignment ( = ), + * and not a simple expression (). + */ + /* we do nothing... */ + } else { + param_count++; + if (param_count == next_param) { + return list->elements[i]; + } + } + } + return NULL; + break; + + case search_op: + for(int i = 0; i < list->n; i++) { + void *res = list->elements[i]->accept(*this); + if (res != NULL) + return res; + } + return NULL; + break; + } /* switch */ + return NULL; + } + + + + void *handle_parameter_assignment(symbol_c *variable_name, symbol_c *expression) { + switch (current_operation) { + case iterate_op: + /* UGLY HACK -> this will be detected in the search_list() function */ + return (void *)this; /* anything, as long as it is not NULL!! */ + break; + + case search_op: + identifier_c *variable_name2 = dynamic_cast(variable_name); + if (variable_name2 == NULL) ERROR; + if (strcasecmp(search_param_name->value, variable_name2->value) == 0) + /* FOUND! This is the same parameter!! */ + return (void *)expression; + return NULL; + break; + } + + ERROR; + return NULL; + } + + + + public: + /* start off at the first parameter once again... */ + void reset(void) { + next_param = param_count = 0; + } + + /* initialise the iterator object. + * We must be given a reference to the function/program/function block call + * that will be analysed... + */ + function_call_param_iterator_c(symbol_c *f_call) { + /* It is expected that f_call will reference one of the following: + * program_configuration_c + * function_invocation_c + * fb_invocation_c + * il_function_call_c + * il_formal_funct_call_c + * ... (have I missed any?) + */ + this->f_call = f_call; + search_param_name = NULL; + reset(); + } + + /* Skip to the next parameter. After object creation, + * the object references on parameter _before_ the first, so + * this function must be called once to get the object to + * reference the first parameter... + * + * Returns whatever is being passed to the parameter! + */ + symbol_c *next(void) { + param_count = 0; + next_param++; + current_operation = function_call_param_iterator_c::iterate_op; + void *res = f_call->accept(*this); + return (symbol_c *)res; + } + + /* Search for the value passed to the parameter named ... */ + symbol_c *search(symbol_c *param_name) { + if (NULL == param_name) ERROR; + search_param_name = dynamic_cast(param_name); + if (NULL == search_param_name) ERROR; + current_operation = function_call_param_iterator_c::search_op; + void *res = f_call->accept(*this); + return (symbol_c *)res; + } + + + + + private: +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ + +/* +CONFIGURATION configuration_name + optional_global_var_declarations + (resource_declaration_list | single_resource_declaration) + optional_access_declarations + optional_instance_specific_initializations +END_CONFIGURATION +*/ +/* +SYM_REF6(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, unused) +*/ + +/* helper symbol for configuration_declaration */ +/* +SYM_LIST(resource_declaration_list_c) +*/ + +/* +RESOURCE resource_name ON resource_type_name + optional_global_var_declarations + single_resource_declaration +END_RESOURCE +*/ +/* +SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration) +*/ + +/* task_configuration_list program_configuration_list */ +/* +SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) +*/ + +/* helper symbol for single_resource_declaration */ +/* +SYM_LIST(task_configuration_list_c) +*/ + +/* helper symbol for single_resource_declaration */ +/* +SYM_LIST(program_configuration_list_c) +*/ + +/* helper symbol for + * - access_path + * - instance_specific_init + */ +/* +SYM_LIST(any_fb_name_list_c) +*/ + +/* [resource_name '.'] global_var_name ['.' structure_element_name] */ +/* +SYM_REF4(global_var_reference_c, resource_name, global_var_name, structure_element_name, unused) +*/ + +/* prev_declared_program_name '.' symbolic_variable */ +/* +SYM_REF2(program_output_reference_c, program_name, symbolic_variable) +*/ + +/* TASK task_name task_initialization */ +/* +SYM_REF2(task_configuration_c, task_name, task_initialization) +*/ + +/* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ +/* +SYM_REF4(task_initialization_c, single_data_source, interval_data_source, priority_data_source, unused) +*/ + +/* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ +// SYM_REF6(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, unused) + void *visit(program_configuration_c *symbol) { + TRACE("program_configuration_c"); + return symbol->prog_conf_elements->accept(*this); + } + +/* prog_conf_elements ',' prog_conf_element */ +// SYM_LIST(prog_conf_elements_c) + void *visit(prog_conf_elements_c *symbol) { + TRACE("prog_conf_elements_c"); + return search_list(symbol); + } + +/* fb_name WITH task_name */ +/* +SYM_REF2(fb_task_c, fb_name, task_name) +*/ + +/* any_symbolic_variable ASSIGN prog_data_source */ +// SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) + void *visit(prog_cnxn_assign_c *symbol) { + TRACE("prog_cnxn_assign_c"); + + /* NOTE: symbolic_variable may be something other than a symbolic_variable_c, but I (Mario) + * do not understand the semantics that should be implmeneted if it is not a + * symbolic_variable, so for the moment we simply give up! + */ + symbolic_variable_c *symb_var = dynamic_cast(symbol->symbolic_variable); + if (NULL == symb_var) + ERROR; + + return handle_parameter_assignment(symb_var->var_name, symbol->prog_data_source); + } + +/* any_symbolic_variable SENDTO data_sink */ +// SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, prog_data_source) + void *visit(prog_cnxn_sendto_c *symbol) { + TRACE("prog_cnxn_sendto_c"); + + /* NOTE: symbolic_variable may be something other than a symbolic_variable_c, but I (Mario) + * do not understand the semantics that should be implmeneted if it is not a + * symbolic_variable, so for the moment we simply give up! + */ + symbolic_variable_c *symb_var = dynamic_cast(symbol->symbolic_variable); + if (NULL == symb_var) + ERROR; + + return handle_parameter_assignment(symb_var->var_name, symbol->prog_data_source); + } + +/* VAR_CONFIG instance_specific_init_list END_VAR */ +/* +SYM_REF2(instance_specific_initializations_c, instance_specific_init_list, unused) +*/ + +/* helper symbol for instance_specific_initializations */ +/* +SYM_LIST(instance_specific_init_list_c) +*/ + +/* resource_name '.' program_name '.' {fb_name '.'} + ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization)) +*/ +/* +SYM_REF6(instance_specific_init_c, resource_name, program_name, any_fb_name_list, variable_name, location, initialization) +*/ + +/* helper symbol for instance_specific_init */ +/* function_block_type_name ':=' structure_initialization */ +/* +SYM_REF2(fb_initialization_c, function_block_type_name, structure_initialization) +*/ + + + + + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +/* | function_name [il_operand_list] */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list) + void *visit(il_function_call_c *symbol) { + TRACE("il_function_call_c"); + if (NULL != symbol->il_operand_list) + return symbol->il_operand_list->accept(*this); + return NULL; + } + + +/* | function_name '(' eol_list [il_param_list] ')' */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list) + void *visit(il_formal_funct_call_c *symbol) { + TRACE("il_formal_funct_call_c"); + if (NULL != symbol->il_param_list) + return symbol->il_param_list->accept(*this); + return NULL; + } + + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list) + void *visit(il_fb_call_c *symbol) { + TRACE("il_fb_call_c"); + /* the following should never occur. In reality the syntax parser + * will guarantee that they never occur, but it makes it easier to + * understand the remaining code :-) + */ + //if ((NULL == symbol->il_operand_list) && (NULL == symbol->il_param_list)) ERROR; + //if ((NULL != symbol->il_operand_list) && (NULL != symbol->il_param_list)) ERROR; + + if (NULL != symbol->il_operand_list) + return symbol->il_operand_list->accept(*this); + if (NULL != symbol->il_param_list) + return symbol->il_param_list->accept(*this); + return NULL; + } + + + +/* | il_operand_list ',' il_operand */ +// SYM_LIST(il_operand_list_c) + void *visit(il_operand_list_c *symbol) { + TRACE("il_operand_list_c"); + return search_list(symbol); + } + + +/* | il_initial_param_list il_param_instruction */ +// SYM_LIST(il_param_list_c) + void *visit(il_param_list_c *symbol) { + TRACE("il_param_list_c"); + return search_list(symbol); + } + +/* il_assign_operator il_operand + * | il_assign_operator '(' eol_list simple_instr_list ')' + */ +// SYM_REF4(il_param_assignment_c, il_assign_operator, il_operand, simple_instr_list, unused) + void *visit(il_param_assignment_c *symbol) { + TRACE("il_param_assignment_c"); + + // TODO : We do not yet handle a instruction list passed as parameter !!! + // since we do not yet support it, it is best to simply stop than to fail silently... + if (NULL != symbol->simple_instr_list) ERROR; + + return handle_parameter_assignment(symbol->il_assign_operator, symbol->il_operand); + } + +/* il_assign_out_operator variable */ +// SYM_REF2(il_param_out_assignment_c, il_assign_out_operator, variable); + void *visit(il_param_out_assignment_c *symbol) { + TRACE("il_param_out_assignment_c"); + return handle_parameter_assignment((symbol_c *)symbol->il_assign_out_operator->accept(*this), symbol->variable); + } + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +/*| [NOT] any_identifier SENDTO */ +// SYM_REF2(il_assign_out_operator_c, option, variable_name) + void *visit(il_assign_out_operator_c *symbol) { + TRACE("il_assign_out_operator_c"); + + // TODO : Handle not_param !!! + // we do not yet support it, so it is best to simply stop than to fail silently... + if (NULL != symbol->option) ERROR; + + return (void *)symbol->variable_name; + } + + + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ + +/* +SYM_REF2(function_invocation_c, function_name, parameter_assignment_list) +*/ + void *visit(function_invocation_c *symbol) { + TRACE("function_invocation_c"); + return symbol->parameter_assignment_list->accept(*this); + } + + + + +/********************/ +/* B 3.2 Statements */ +/********************/ + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +/* +SYM_REF2(assignment_statement_c, l_exp, r_exp) +*/ + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +/* RETURN */ +// SYM_REF0(return_statement_c) + + +/* fb_name '(' [param_assignment_list] ')' */ +/* param_assignment_list -> may be NULL ! */ +// SYM_REF2(fb_invocation_c, fb_name, param_assignment_list) + void *visit(fb_invocation_c *symbol) { + TRACE("fb_invocation_c"); + return symbol->param_assignment_list->accept(*this); + } + +/* helper symbol for fb_invocation */ +/* param_assignment_list ',' param_assignment */ +// SYM_LIST(param_assignment_list_c) + void *visit(param_assignment_list_c *symbol) { + TRACE("param_assignment_list_c"); + return search_list(symbol); + } + +/* variable_name ASSIGN expression */ +// SYM_REF2(input_variable_param_assignment_c, variable_name, expression) + void *visit(input_variable_param_assignment_c *symbol) { + TRACE("input_variable_param_assignment_c"); + return handle_parameter_assignment(symbol->variable_name, symbol->expression); + } + +/* [NOT] variable_name '=>' variable */ +// SYM_REF4(output_variable_param_assignment_c, not_param, variable_name, variable, unused) + void *visit(output_variable_param_assignment_c *symbol) { + TRACE("output_variable_param_assignment_c"); + // TODO : Handle not_param !!! + if (NULL != symbol->not_param) ERROR; // we do not yet support it, so it is best to simply stop than to fail silently... + + return handle_parameter_assignment(symbol->variable_name, symbol->variable); + } + +/* helper CLASS for output_variable_param_assignment */ +// SYM_REF0(not_paramassign_c) +// TODO... ??? + + + + + +}; + + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/function_param_iterator.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/function_param_iterator.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,426 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Function parameter iterator. + * Iterate through the in/out parameters of a function declaration. + * Function blocks are also suported. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + +//#include "generate_cc.hh" + + +#include "../../absyntax/visitor.hh" + + +/* given a function_declaration_c, iterate through each + * function in/out/inout parameter, returning the name + * of each parameter...function_param_iterator_c + */ +class function_param_iterator_c : public null_visitor_c { + public: + /* A type to specify the type of parameter. + * VAR_INPUT => direction_in + * VAR_OUTPUT => direction_out + * VAR_IN_OUT => direction_inout + * VAR_EXTERNAL => direction_extref + * + * Note that VAR_EXTERNAL declares variables that are in reality references + * to global variables. This is used only inside programs! + * These references to external variables must be correctly initialised to refer + * to the correct global variable. Note that the user may define which variable is to be + * referenced in a CONFIGURATION, and that different instantiations of a program + * may have the same external variable reference diffenrent global variables! + * The references must therefore be correctly initialised when the program instance + * is created. This may be done by the PROGRAM class constructor since the ST and IL + * languages do not allow the VAR_EXTERNAL reference to change at runtime + * for a specific instance. + * + * We therefore need to call a PROGRAM class constructor with the variables + * that should be refernced by the VAR_EXTERNAL variables. The direction_extref will + * be used to identify these parameters! + */ + typedef enum {direction_in, direction_out, direction_inout, direction_extref} param_direction_t ; + + + private: + /* a pointer to the function_block_declaration_c + * or function_declaration_c currently being analysed. + */ + symbol_c *f_decl; + int next_param, param_count; + identifier_c *current_param_name; + symbol_c *current_param_type; + symbol_c *current_param_default_value; + param_direction_t current_param_direction; + + private: + void* handle_param_list(list_c *list) { + if (next_param <= param_count + list->n) + return list->elements[next_param - param_count - 1]; + + /* the desired param is not on this list... */ + param_count += list->n; + return NULL; + } + + void* handle_single_param(symbol_c *var_name) { + param_count++; + if (next_param == param_count) + return var_name; + + /* not yet the desired param... */ + return NULL; + } + + void* iterate_list(list_c *list) { + void *res; + for (int i = 0; i < list->n; i++) { + res = list->elements[i]->accept(*this); + if (res != NULL) + return res; + } + return NULL; + } + + + public: + /* start off at the first parameter once again... */ + void reset(void) { + next_param = param_count = 0; + current_param_name = NULL; + current_param_type = current_param_default_value = NULL; + } + + /* initialise the iterator object. + * We must be given a reference to the function declaration + * that will be analysed... + */ + function_param_iterator_c(function_declaration_c *f_decl) { + this->f_decl = f_decl; + reset(); + } + + /* initialise the iterator object. + * We must be given a reference to the function block declaration + * that will be analysed... + */ + function_param_iterator_c(function_block_declaration_c *fb_decl) { + this->f_decl = fb_decl; + reset(); + } + + /* initialise the iterator object. + * We must be given a reference to the program declaration + * that will be analysed... + */ + function_param_iterator_c(program_declaration_c *p_decl) { + this->f_decl = p_decl; + reset(); + } + + /* Skip to the next parameter. After object creation, + * the object references on parameter _before_ the first, so + * this function must be called once to get the object to + * reference the first parameter... + * + * Returns the parameter's name! + */ + identifier_c *next(void) { + void *res; + param_count = 0; + next_param++; + res = f_decl->accept(*this); + if (res == NULL) + return NULL; + + symbol_c *sym = (symbol_c *)res; + identifier_c *identifier = dynamic_cast(sym); + if (identifier == NULL) + ERROR; + + current_param_name = identifier; + return current_param_name; + } + + /* Returns the currently referenced parameter's default value, + * or NULL if none is specified in the function declrataion itself. + */ + symbol_c *default_value(void) { + return current_param_default_value; + } + + /* Returns the currently referenced parameter's type name. */ + symbol_c *param_type(void) { + return current_param_type; + } + + /* Returns the currently referenced parameter's data passing direction. + * i.e. VAR_INPUT, VAR_OUTPUT or VAR_INOUT + */ + param_direction_t param_direction(void) { + return current_param_direction; + } + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + void *visit(input_declarations_c *symbol) { + TRACE("input_declarations_c"); + current_param_direction = direction_in; + return symbol->input_declaration_list->accept(*this); + } + void *visit(input_declaration_list_c *symbol) {TRACE("input_declaration_list_c"); return iterate_list(symbol);} + void *visit(edge_declaration_c *symbol) {TRACE("edge_declaration_c"); return symbol->var1_list->accept(*this);} + +#if 0 +/* var1_list ':' array_spec_init */ +SYM_REF2(array_var_init_decl_c, var1_list, array_spec_init) + +/* var1_list ':' initialized_structure */ +SYM_REF2(structured_var_init_decl_c, var1_list, initialized_structure) + +/* name_list ':' function_block_type_name ASSIGN structure_initialization */ +/* structure_initialization -> may be NULL ! */ +SYM_REF4(fb_name_decl_c, fb_name_list, function_block_type_name, structure_initialization, unused) + +/* name_list ',' fb_name */ +SYM_LIST(fb_name_list_c) +#endif + + void *visit(output_declarations_c *symbol) { + TRACE("output_declarations_c"); + current_param_direction = direction_out; + return symbol->var_init_decl_list->accept(*this); + } + void *visit(input_output_declarations_c *symbol) { + TRACE("input_output_declarations_c"); + current_param_direction = direction_inout; + return symbol->var_declaration_list->accept(*this); + } + void *visit(var_declaration_list_c *symbol) {TRACE("var_declaration_list_c"); return iterate_list(symbol);} + +#if 0 +/* var1_list ':' array_specification */ +SYM_REF2(array_var_declaration_c, var1_list, array_specification) + +/* var1_list ':' structure_type_name */ +SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) +#endif + +/* VAR [CONSTANT] var_init_decl_list END_VAR */ + void *visit(var_declarations_c *symbol) {TRACE("var_declarations_c"); return NULL;} + +#if 0 +/* VAR RETAIN var_init_decl_list END_VAR */ +SYM_REF2(retentive_var_declarations_c, var_init_decl_list, unused) + +/* VAR [CONSTANT|RETAIN|NON_RETAIN] located_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +SYM_REF2(located_var_declarations_c, option, located_var_decl_list) + +/* helper symbol for located_var_declarations */ +/* located_var_decl_list located_var_decl ';' */ +SYM_LIST(located_var_decl_list_c) + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +SYM_REF4(located_var_decl_c, variable_name, location, located_var_spec_init, unused) +#endif + +/*| VAR_EXTERNAL [CONSTANT] external_declaration_list END_VAR */ +/* option -> may be NULL ! */ +// SYM_REF2(external_var_declarations_c, option, external_declaration_list) + void *visit(external_var_declarations_c *symbol) { + TRACE("external_var_declarations_c"); + current_param_direction = direction_extref; + return symbol->external_declaration_list->accept(*this); + } + +/* helper symbol for external_var_declarations */ +/*| external_declaration_list external_declaration';' */ +// SYM_LIST(external_declaration_list_c) + void *visit(external_declaration_list_c *symbol) {TRACE("external_declaration_list_c"); return iterate_list(symbol);} + +/* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ +//SYM_REF2(external_declaration_c, global_var_name, specification) + void *visit(external_declaration_c *symbol) { + TRACE("external_declaration_c"); + /* It is OK to store these values in the current_param_XXX + * variables, because if the desired parameter is not in the + * variable list we will be analysing, the current_param_XXXX + * variables will get overwritten when we visit the next + * var1_init_decl_c list! + */ + current_param_default_value = spec_init_sperator_c::get_init(symbol->specification); + current_param_type = spec_init_sperator_c::get_spec(symbol->specification); + + return handle_single_param(symbol->global_var_name); + } + + +#if 0 +/*| VAR_GLOBAL [CONSTANT|RETAIN] global_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +SYM_REF2(global_var_declarations_c, option, global_var_decl_list) + +/* helper symbol for global_var_declarations */ +/*| global_var_decl_list global_var_decl ';' */ +SYM_LIST(global_var_decl_list_c) + +/*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */ +/* type_specification ->may be NULL ! */ +SYM_REF2(global_var_decl_c, global_var_spec, type_specification) + +/*| global_var_name location */ +SYM_REF2(global_var_spec_c, global_var_name, location) + +/* AT direct_variable */ +SYM_REF2(location_c, direct_variable, unused) + +/*| global_var_list ',' global_var_name */ +SYM_LIST(global_var_list_c) + +/* var1_list ':' single_byte_string_spec */ +SYM_REF2(single_byte_string_var_declaration_c, var1_list, single_byte_string_spec) + +/* STRING ['[' integer ']'] [ASSIGN single_byte_character_string] */ +/* integer ->may be NULL ! */ +/* single_byte_character_string ->may be NULL ! */ +SYM_REF2(single_byte_string_spec_c, integer, single_byte_character_string) + +/* var1_list ':' double_byte_string_spec */ +SYM_REF2(double_byte_string_var_declaration_c, var1_list, double_byte_string_spec) + +/* WSTRING ['[' integer ']'] [ASSIGN double_byte_character_string] */ +/* integer ->may be NULL ! */ +/* double_byte_character_string ->may be NULL ! */ +SYM_REF2(double_byte_string_spec_c, integer, double_byte_character_string) + +/*| VAR [RETAIN|NON_RETAIN] incompl_located_var_decl_list END_VAR */ +/* option ->may be NULL ! */ +SYM_REF2(incompl_located_var_declarations_c, option, incompl_located_var_decl_list) + +/* helper symbol for incompl_located_var_declarations */ +/*| incompl_located_var_decl_list incompl_located_var_decl ';' */ +SYM_LIST(incompl_located_var_decl_list_c) + +/* variable_name incompl_location ':' var_spec */ +SYM_REF4(incompl_located_var_decl_c, variable_name, incompl_location, var_spec, unused) + +/* AT incompl_location_token */ +SYM_TOKEN(incompl_location_c) +#endif + + + void *visit(var1_init_decl_c *symbol) { + TRACE("var1_init_decl_c"); + /* It is OK to store these values in the current_param_XXX + * variables, because if the desired parameter is not in the + * variable list we will be analysing, the current_param_XXXX + * variables will get overwritten when we visit the next + * var1_init_decl_c list! + */ + current_param_default_value = spec_init_sperator_c::get_init(symbol->spec_init); + current_param_type = spec_init_sperator_c::get_spec(symbol->spec_init); + + return symbol->var1_list->accept(*this); + } + + + + void *visit(var1_list_c *symbol) { + TRACE("var1_list_c"); + return handle_param_list(symbol); + } + + void *visit(var_init_decl_list_c *symbol) {TRACE("var_init_decl_list_c"); return iterate_list(symbol);} + + +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ + void *visit(function_declaration_c *symbol) {TRACE("function_declaration_c"); return symbol->var_declarations_list->accept(*this);} + /* intermediate helper symbol for function_declaration */ + void *visit(var_declarations_list_c *symbol) {TRACE("var_declarations_list_c"); return iterate_list(symbol);} + void *visit(function_var_decls_c *symbol) {TRACE("function_var_decls_c"); /* ignore */ return NULL;} + + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ +/* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ + void *visit(function_block_declaration_c *symbol) {TRACE("function_block_declaration_c"); return symbol->var_declarations->accept(*this);} + +/* intermediate helper symbol for function_declaration */ +/* { io_var_declarations | other_var_declarations } */ +/* + * NOTE: we re-use the var_declarations_list_c + */ + +/* VAR_TEMP temp_var_decl_list END_VAR */ + void *visit(temp_var_decls_c *symbol) {TRACE("temp_var_decls_c"); /* ignore */ return NULL;} + void *visit(temp_var_decls_list_c *symbol) {TRACE("temp_var_decls_list_c"); /* ignore */ return NULL;} + +/* VAR NON_RETAIN var_init_decl_list END_VAR */ + void *visit(non_retentive_var_decls_c *symbol) {TRACE("non_retentive_var_decls_c"); /* ignore */ return NULL;} + + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +/* PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */ +// SYM_REF4(program_declaration_c, program_type_name, var_declarations, function_block_body, unused) + void *visit(program_declaration_c *symbol) {TRACE("program_declaration_c"); return symbol->var_declarations->accept(*this);} + +/* intermediate helper symbol for program_declaration_c */ +/* { io_var_declarations | other_var_declarations } */ +/* + * NOTE: we re-use the var_declarations_list_c + */ + +}; + + + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,1010 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * This is one of the versions available for the 4th stage. + * + * This 4th stage generates a c++ source program equivalent + * to the IL and ST code. + */ + + + + + + +// #include /* required for NULL */ +#include +#include + +#include "../../util/symtable.hh" +#include "../../util/dsymtable.hh" +#include "../../absyntax/visitor.hh" + +#include "../stage4.hh" + + + + + + + + +//#define DEBUG +#ifdef DEBUG +#define TRACE(classname) printf("\n____%s____\n",classname); +#else +#define TRACE(classname) +#endif + + + +#define ERROR error_exit(__FILE__,__LINE__) +/* function defined in main.cc */ +extern void error_exit(const char *file_name, int line_no); + + + + + + + +/* A symbol table with all globally declared functions... */ +function_declaration_c null_symbol1(NULL,NULL,NULL,NULL); +dsymtable_c function_symtable; + +/* A symbol table with all globally declared functions block types... */ +function_block_declaration_c null_symbol2(NULL,NULL,NULL,NULL); +symtable_c function_block_type_symtable; + +/* A symbol table with all globally declared program types... */ +program_declaration_c null_symbol3(NULL,NULL,NULL,NULL); +symtable_c program_type_symtable; + +/* A symbol table with all user declared type definitions... */ +/* Note that function block types and program types have their + * own symbol tables, so do not get placed in this symbol table! + */ +symbol_c null_symbol4; +symtable_c type_symtable; + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + +/* returns 0 if the names are equal!! */ +/* NOTE: it must ignore case!! */ +static int compare_identifiers(symbol_c *ident1, symbol_c *ident2) { + + identifier_c *name1 = dynamic_cast(ident1); + identifier_c *name2 = dynamic_cast(ident2); + + if ((name1 == NULL) || (name2 == NULL)) + /* invalid identifiers... */ + return -1; + + if (strcasecmp(name1->value, name2->value) == 0) + return 0; + + /* identifiers do not match! */ + return 1; +} + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +/* Unlike Programs and Configurations which get mapped onto C++ classes, + * Function Blocks are mapped onto a C structure containing the variables, and + * a C function containing the code in the FB's body. This is to allow direct allocation + * of a FB variable (which is really an instance of the C data structure) to + * a member of a union variable (note that classes with constructors cannot + * be mebers of a union), which is done in IL when loading a FB onto IL's + * default variable. + * + * So as not to clash the names of the C data structure and the C function, + * the C structure is given a name identical to that of the FB name, whereas + * the name of the function is the FB name with a constant string appended. + * The value of that constant string which is appended is defined in the following + * constant. + * In order not to clash with any variable in the IL and ST source codem the + * following constant should contain a double underscore, which is not allowed + * in IL and ST. + * + * e.g.: FUNTION_BLOCK TEST + * is mapped onto a TEST data structure, and a TEST_body__ function. + */ + +#define FB_FUNCTION_SUFFIX "_body__" + +/* The FB body function is passed as the only parameter a pointer to the FB data + * structure instance. The name of this parameter is given by the following cosntant. + * In order not to clash with any variable in the IL and ST source codem the + * following constant should contain a double underscore, which is not allowed + * in IL and ST. + * + * e.g.: the body of FUNTION_BLOCK TEST + * is mapped onto the C function + * TEST_body__(TEST *data__) + */ + +#define FB_FUNCTION_PARAM "data__" + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + +#include "spec_init_separator.cc" +#include "function_param_iterator.cc" +#include "function_call_iterator.cc" +#include "function_call_param_iterator.cc" +#include "type_initial_value.cc" +#include "search_fb_instance_decl.cc" +#include "search_base_type.cc" +#include "search_var_instance_decl.cc" +#include "decompose_var_instance_name.cc" +#include "search_varfb_instance_type.cc" +#include "search_constant_type.cc" + +#include "generate_cc_base.cc" +#include "generate_cc_typedecl.cc" +#include "generate_cc_vardecl.cc" +#include "generate_cc_configbody.cc" + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +/* Generate a name for a temporary variable. + * Each new name generated is appended a different number, + * starting off from 0. + * After calling reset(), the names will start off again from 0. + */ +#define VAR_LEADER "__" +#define TEMP_VAR VAR_LEADER "TMP_" +#define SOURCE_VAR VAR_LEADER "SRC_" + +#include "generate_cc_tempvardecl.cc" + +#include "generate_cc_st.cc" +#include "generate_cc_il.cc" + +#include "generate_cc.hh" + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +/* A helper class that knows how to generate code for both the IL and ST languages... */ +class generate_cc_IL_and_ST_c: public null_visitor_c { + private: + stage4out_c *s4o_ptr; + symbol_c *scope; + const char *variable_prefix; + + public: + generate_cc_IL_and_ST_c(stage4out_c *s4o_ptr, symbol_c *scope, const char *variable_prefix = NULL) { + if (NULL == scope) ERROR; + this->s4o_ptr = s4o_ptr; + this->scope = scope; + this->variable_prefix = variable_prefix; + } + + + public: +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ + +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +/*| instruction_list il_instruction */ +void *visit(instruction_list_c *symbol) { + generate_cc_il_c generate_cc_il(s4o_ptr, scope, variable_prefix); + generate_cc_il.generate(symbol); + return NULL; +} + +/* Remainder implemented in generate_cc_il_c... */ + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +/* Implemented in generate_cc_st_c */ + +/********************/ +/* B 3.2 Statements */ +/********************/ +void *visit(statement_list_c *symbol) { + generate_cc_st_c generate_cc_st(s4o_ptr, scope, variable_prefix); + generate_cc_st.generate(symbol); + return NULL; +} + +/* Remainder implemented in generate_cc_st_c... */ +}; + + + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + +class generate_cc_c: public generate_cc_typedecl_c { + + public: + generate_cc_c(stage4out_c *s4o_ptr) + : generate_cc_typedecl_c(s4o_ptr) {}; + virtual ~generate_cc_c(void) {} + + + public: +/***************************/ +/* B 0 - Programming Model */ +/***************************/ +void *visit(library_c *symbol) { + TRACE("library_c"); + + /* Insert the header... */ + s4o.print("/*******************************************/\n"); + s4o.print("/* FILE GENERATED BY iec2cc */\n"); + s4o.print("/* Editing this file is not recommended... */\n"); + s4o.print("/*******************************************/\n"); + s4o.print("\n\n\n\n\n"); + s4o.print("#include \"plciec.h\"\n"); + s4o.print("\n\n\n\n\n"); + + /* now do the actual code... */ + print_list(symbol); + s4o.print("\n\n"); + + /* Finish off with the main() */ + s4o.print("#include \"plciec.cc\"\n"); + s4o.print("\n\n"); + + // function_symtable.print(); + return NULL; +} + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ + /* done in base class(es) */ + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + /* originally empty... */ + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ + /* done in base class(es) */ + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ + /* done in base class(es) */ + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ + /* done in base class(es) */ + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ + /* done in base class(es) */ + +/**********************/ +/* B.1.3 - Data types */ +/**********************/ +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ + /* done in base class(es) */ + +/********************************/ +/* B.1.3.2 - Generic data types */ +/********************************/ + /* originally empty... */ + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + /* done in base class(es) */ + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ + /* done in base class(es) */ + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ + /* done in base class(es) */ + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ + /* done in base class(es) */ + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + /* done in base class(es) */ + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ + +public: +/* FUNCTION derived_function_name ':' elementary_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */ +/* | FUNCTION derived_function_name ':' derived_type_name io_OR_function_var_declarations_list function_body END_FUNCTION */ +void *visit(function_declaration_c *symbol) { + generate_cc_vardecl_c *vardecl; + TRACE("function_declaration_c"); + + /* start off by adding this declaration to the global + * function declaration symbol table... + */ + function_symtable.insert(symbol->derived_function_name, symbol); + + /* (A) Function declaration... */ + /* (A.1) Function return type */ + s4o.print("// FUNCTION\n"); + symbol->type_name->accept(*this); /* return type */ + s4o.print(" "); + /* (A.2) Function name */ + symbol->derived_function_name->accept(*this); + s4o.print("("); + + /* (A.3) Function parameters */ + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::finterface_vf, + generate_cc_vardecl_c::input_vt | + generate_cc_vardecl_c::output_vt | + generate_cc_vardecl_c::inoutput_vt); + vardecl->print(symbol->var_declarations_list); + delete vardecl; + s4o.indent_left(); + + s4o.print(")\n" + s4o.indent_spaces + "{\n"); + + /* (B) Function local variable declaration */ + /* (B.1) Variables declared in ST source code */ + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, generate_cc_vardecl_c::localinit_vf, generate_cc_vardecl_c::private_vt); + vardecl->print(symbol->var_declarations_list); + delete vardecl; + + /* (B.2) Temporary variable for function's return value */ + /* It will have the same name as the function itself! */ + s4o.print(s4o.indent_spaces); + symbol->type_name->accept(*this); /* return type */ + s4o.print(" "); + symbol->derived_function_name->accept(*this); + s4o.print(" = "); + { + /* get the default value of this variable's type */ + symbol_c *default_value = (symbol_c *)symbol->type_name->accept(*type_initial_value_c::instance()); + if (default_value == NULL) ERROR; + default_value->accept(*this); + } + s4o.print(";\n\n"); + + /* (C) Function body */ + generate_cc_IL_and_ST_c generate_cc_code(&s4o, symbol); + symbol->function_body->accept(generate_cc_code); + s4o.print(s4o.indent_spaces + "return "); + symbol->derived_function_name->accept(*this); + s4o.print(";\n"); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "}\n\n\n"); + + return NULL; +} + + +/* The remaining var_declarations_list_c, function_var_decls_c + * and var2_init_decl_list_c are handled in the generate_cc_vardecl_c class + */ + + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ +public: +/* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ +//SYM_REF4(function_block_declaration_c, fblock_name, var_declarations, fblock_body, unused) +void *visit(function_block_declaration_c *symbol) { + generate_cc_vardecl_c *vardecl; + TRACE("function_block_declaration_c"); + + /* start off by adding this declaration to the global + * function block declaration symbol table... + */ + function_block_type_symtable.insert(symbol->fblock_name, symbol); + + /* (A) Function Block data structure declaration... */ + /* (A.1) Data structure declaration */ + s4o.print("// FUNCTION_BLOCK "); + symbol->fblock_name->accept(*this); + s4o.print("\n// Data part\n"); + s4o.print("typedef struct {\n"); + s4o.indent_right(); + /* (A.2) Public variables: i.e. the function parameters... */ + s4o.print(s4o.indent_spaces + "// FB Interface - IN, OUT, IN_OUT variables\n"); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::local_vf, + generate_cc_vardecl_c::input_vt | + generate_cc_vardecl_c::output_vt | + generate_cc_vardecl_c::inoutput_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.print("\n"); + /* (A.3) Private internal variables */ + s4o.print(s4o.indent_spaces + "// FB private variables - TEMP, private and located variables\n"); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::local_vf, + generate_cc_vardecl_c::temp_vt | + generate_cc_vardecl_c::private_vt | + generate_cc_vardecl_c::located_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.print("\n"); + + /* (A.4) Function Block data structure type name. */ + s4o.indent_left(); + s4o.print("} "); + symbol->fblock_name->accept(*this); + s4o.print(";\n\n"); + + + /* (B) Function with FB body */ + /* (B.1) Function declaration */ + s4o.print("// Code part\n"); + /* function interface */ + s4o.print("void "); + symbol->fblock_name->accept(*this); + s4o.print(FB_FUNCTION_SUFFIX); + s4o.print("("); + /* first and only parameter is a pointer to the data */ + symbol->fblock_name->accept(*this); + s4o.print(" *"); + s4o.print(FB_FUNCTION_PARAM); + s4o.print(") {\n"); + s4o.indent_right(); + + /* (B.2) Initialize TEMP variables */ + /* function body */ + s4o.print(s4o.indent_spaces + "// Initialise TEMP variables\n"); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::init_vf, + generate_cc_vardecl_c::temp_vt); + vardecl->print(symbol->var_declarations, NULL, FB_FUNCTION_PARAM"->"); + delete vardecl; + s4o.print("\n"); + + /* (B.3) Function code */ + generate_cc_IL_and_ST_c generate_cc_code(&s4o, symbol, FB_FUNCTION_PARAM"->"); + symbol->fblock_body->accept(generate_cc_code); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "} // "); + symbol->fblock_name->accept(*this); + s4o.print(FB_FUNCTION_SUFFIX); + s4o.print(s4o.indent_spaces + "() \n\n"); + + + + + + + +#if 0 + ++++++++++++++++++++++++++++++++++++++ + s4o.print(s4o.indent_spaces + "class "); + symbol->fblock_name->accept(*this); + s4o.print(" {\n"); + s4o.indent_right(); + + /* (A.2) Public variables: i.e. the function parameters... */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::local_vf, + generate_cc_vardecl_c::input_vt | + generate_cc_vardecl_c::output_vt | + generate_cc_vardecl_c::inoutput_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.indent_left(); + s4o.print("\n"); + + /* (A.3) Private internal variables */ + s4o.print(s4o.indent_spaces + "private:\n"); + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::local_vf, + generate_cc_vardecl_c::private_vt | + generate_cc_vardecl_c::located_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.indent_left(); + s4o.print("\n"); + +--------------------------------- + /* (B) Constructor */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + symbol->fblock_name->accept(*this); + s4o.print("(void)\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::constructorinit_vf, + generate_cc_vardecl_c::input_vt | + generate_cc_vardecl_c::output_vt | + generate_cc_vardecl_c::inoutput_vt | + generate_cc_vardecl_c::private_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.print("\n" + s4o.indent_spaces + "{}\n\n"); + s4o.indent_left(); + s4o.indent_left(); +--------------------------------- + + + /* (C) Public Function*/ + /* (C.1) Public Function declaration */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces + "void f(void) {\n"); + + /* (C.2) Temporary variables */ + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, generate_cc_vardecl_c::localinit_vf, generate_cc_vardecl_c::temp_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.indent_left(); + s4o.print("\n"); + + /* (C.3) Public Function body */ + s4o.indent_right(); + this->current_scope = symbol; + symbol->fblock_body->accept(*this); + this->current_scope = NULL; + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "} /* f() */\n\n"); + s4o.indent_left(); + + /* (D) Close the class declaration... */ + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "}; /* class "); + symbol->fblock_name->accept(*this); + s4o.print(" */\n"); + +--------------------------------- + /* (E) Initialise the static member variables... */ + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::globalinit_vf, + generate_cc_vardecl_c::located_vt); + vardecl->print(symbol->var_declarations, symbol->fblock_name); + delete vardecl; + +#endif + + s4o.indent_left(); + s4o.print("\n\n\n\n"); + + return NULL; +} + + +/* The remaining temp_var_decls_c, temp_var_decls_list_c + * and non_retentive_var_decls_c are handled in the generate_cc_vardecl_c class + */ + + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ + + + +public: +/* PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */ +//SYM_REF4(program_declaration_c, program_type_name, var_declarations, function_block_body, unused) +void *visit(program_declaration_c *symbol) { + generate_cc_vardecl_c *vardecl; + TRACE("program_declaration_c"); + + /* start off by adding this declaration to the global + * program declaration symbol table... + */ + program_type_symtable.insert(symbol->program_type_name, symbol); + + /* (A) Class (Function Block) declaration... */ + /* (A.1) Class (Function Block) name */ + s4o.print("// PROGRAM\n"); + s4o.print(s4o.indent_spaces + "class "); + symbol->program_type_name->accept(*this); + s4o.print(" {\n"); + s4o.indent_right(); + + /* (A.2) Public variables: i.e. the program parameters... */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::local_vf, + generate_cc_vardecl_c::input_vt | + generate_cc_vardecl_c::output_vt | + generate_cc_vardecl_c::inoutput_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.indent_left(); + s4o.print("\n"); + + /* (A.3) Private internal variables */ + s4o.print(s4o.indent_spaces + "private:\n"); + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::local_vf, + generate_cc_vardecl_c::private_vt | + generate_cc_vardecl_c::located_vt | + generate_cc_vardecl_c::external_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.indent_left(); + s4o.print("\n"); + + /* (B) Constructor */ + /* (B.1) Constructor name... */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + symbol->program_type_name->accept(*this); + + /* (B.2) Constructor parameters (i.e. the external variables)... */ + s4o.print("("); + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::finterface_vf, + generate_cc_vardecl_c::external_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.print(")\n"); + s4o.indent_left(); + + /* (B.2) Member initializations... */ + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::constructorinit_vf, + generate_cc_vardecl_c::input_vt | + generate_cc_vardecl_c::output_vt | + generate_cc_vardecl_c::inoutput_vt | + generate_cc_vardecl_c::private_vt | + generate_cc_vardecl_c::external_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.print("\n" + s4o.indent_spaces + "{}\n\n"); + s4o.indent_left(); + s4o.indent_left(); + + /* (C) Public Function*/ + /* (C.1) Public Function declaration */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces + "void f(void) {\n"); + + /* (C.2) Temporary variables */ + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, generate_cc_vardecl_c::localinit_vf, generate_cc_vardecl_c::temp_vt); + vardecl->print(symbol->var_declarations); + delete vardecl; + s4o.indent_left(); + s4o.print("\n"); + + /* (C.3) Public Function body */ + s4o.indent_right(); + generate_cc_IL_and_ST_c generate_cc_code(&s4o, symbol); + symbol->function_block_body->accept(generate_cc_code); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "} /* f() */\n\n"); + s4o.indent_left(); + + /* (D) Close the class declaration... */ + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "}; /* class "); + symbol->program_type_name->accept(*this); + s4o.print(" */\n\n\n"); + + /* (E) Initialise the static member variables... */ + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::globalinit_vf, + generate_cc_vardecl_c::located_vt); + vardecl->print(symbol->var_declarations, symbol->program_type_name); + delete vardecl; + s4o.print("\n\n\n\n"); + + return NULL; +} + + +/* intermediate helper symbol for program_declaration_c */ +/* { io_var_declarations | other_var_declarations } */ +/* + * NOTE: we re-use the var_declarations_list_c + */ + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ + + +public: +/* +CONFIGURATION configuration_name + optional_global_var_declarations + (resource_declaration_list | single_resource_declaration) + optional_access_declarations + optional_instance_specific_initializations +END_CONFIGURATION +*/ +/* +SYM_REF6(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, unused) +*/ +void *visit(configuration_declaration_c *symbol) { + static int configuration_count = 0; + generate_cc_vardecl_c *vardecl; + TRACE("configuration_declaration_c"); + + configuration_count++; + if (configuration_count == 1) { + /* the first configuration is the one we will use!! */ + s4o.print("#define __configuration_c "); + symbol->configuration_name->accept(*this); + s4o.print("\n" + s4o.indent_spaces); + } + + /* (A) Class (__configuration) declaration... */ + /* (A.1) configuration name in comment */ + s4o.print("// CONFIGURATION\n" + s4o.indent_spaces); + s4o.print("class "); + symbol->configuration_name->accept(*this); + s4o.print(" {\n"); + s4o.indent_right(); + + /* (A.2) Global variables + * AND + * (A.3) Programs in the Configuration + */ + /* Programs types are mapped onto classes, + * and programs are then instantiated inside the configuration + * as objects of the appropriate class! + */ + s4o.print(s4o.indent_spaces + "private:\n"); + s4o.indent_right(); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::local_vf, + generate_cc_vardecl_c::global_vt | + generate_cc_vardecl_c::program_vt | + generate_cc_vardecl_c::resource_vt); + vardecl->print(symbol); + delete vardecl; + s4o.indent_left(); + s4o.print("\n"); + + + + /* (B) Constructor */ + /* (B.1) Constructor name... */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + symbol->configuration_name->accept(*this); + s4o.print("(void)\n"); + + /* (B.2) Member initializations... */ + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + vardecl = new generate_cc_vardecl_c(&s4o, + generate_cc_vardecl_c::constructorinit_vf, + generate_cc_vardecl_c::program_vt | + generate_cc_vardecl_c::global_vt | + generate_cc_vardecl_c::resource_vt); + vardecl->print(symbol); + delete vardecl; + + /* (B.3) Constructor Body... */ + s4o.print("\n" + s4o.indent_spaces + "{}\n\n"); + s4o.indent_left(); + s4o.indent_left(); + + /* (C) Public Function*/ + /* (C.1) Public Function declaration */ + s4o.print(s4o.indent_spaces + "public:\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces + "void run(void) {\n"); + + /* (C.2) Public Function body */ + /* Invoke each program in the configuration */ + s4o.indent_right(); + generate_cc_configbody_c *configbody = new generate_cc_configbody_c(&s4o); + symbol->accept(*configbody); + delete configbody; + s4o.indent_left(); + + /* (C.3) Close Public Function body */ + s4o.print(s4o.indent_spaces + "} /* f() */\n\n"); + s4o.indent_left(); + + /* (D) Close the class declaration... */ + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "}; /* class "); + symbol->configuration_name->accept(*this); + s4o.print(" */\n\n\n"); + + return NULL; +} + + + + + + + +#if 0 + +/* helper symbol for configuration_declaration */ +SYM_LIST(resource_declaration_list_c) + +/* +RESOURCE resource_name ON resource_type_name + optional_global_var_declarations + single_resource_declaration +END_RESOURCE +*/ +SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration) + +/* task_configuration_list program_configuration_list */ +SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) + +/* helper symbol for single_resource_declaration */ +SYM_LIST(task_configuration_list_c) + +/* helper symbol for single_resource_declaration */ +SYM_LIST(program_configuration_list_c) + +/* helper symbol for + * - access_path + * - instance_specific_init + */ +SYM_LIST(any_fb_name_list_c) + +/* [resource_name '.'] global_var_name ['.' structure_element_name] */ +SYM_REF4(global_var_reference_c, resource_name, global_var_name, structure_element_name, unused) + +/* prev_declared_program_name '.' symbolic_variable */ +SYM_REF2(program_output_reference_c, program_name, symbolic_variable) + +/* TASK task_name task_initialization */ +SYM_REF2(task_configuration_c, task_name, task_initialization) + +/* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ +SYM_REF4(task_initialization_c, single_data_source, interval_data_source, priority_data_source, unused) + +/* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ +SYM_REF6(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, unused) + +/* prog_conf_elements ',' prog_conf_element */ +SYM_LIST(prog_conf_elements_c) + +/* fb_name WITH task_name */ +SYM_REF2(fb_task_c, fb_name, task_name) + +/* any_symbolic_variable ASSIGN prog_data_source */ +SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) + +/* any_symbolic_variable SENDTO data_sink */ +SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, prog_data_source) + +/* VAR_CONFIG instance_specific_init_list END_VAR_BOGUS */ +SYM_REF2(instance_specific_initializations_c, instance_specific_init_list, unused) + +/* helper symbol for instance_specific_initializations */ +SYM_LIST(instance_specific_init_list_c) + +/* resource_name '.' program_name '.' {fb_name '.'} + ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization)) +*/ +SYM_REF6(instance_specific_init_c, resource_name, program_name, any_fb_name_list, variable_name, location, initialization) + +/* helper symbol for instance_specific_init */ +/* function_block_type_name ':=' structure_initialization */ +SYM_REF2(fb_initialization_c, function_block_type_name, structure_initialization) + +#endif + + +}; /* generate_cc_c */ + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + + +visitor_c *new_code_generator(stage4out_c *s4o) {return new generate_cc_c(s4o);} +void delete_code_generator(visitor_c *code_generator) {delete code_generator;} + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc.hh Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,56 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * This is one of the versions available for the 4th stage. + * + * This 4th stage generates a c++ source program equivalent + * to the IL and ST code. + */ + + +/* + * generate_cc.HH + */ + + +#ifndef _generate_cc_HH +#define _generate_cc_HH + + + +#include +#include "../../absyntax/absyntax.hh" +#include "../../absyntax/visitor.hh" + + + + + + + + +#endif /* _generate_cc_HH */ + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc_base.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc_base.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,636 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Conversion of basic abstract syntax constructs. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + +//#include "generate_cc.hh" + + + + + + + + + + + + + +class generate_cc_base_c: public iterator_visitor_c { + + protected: + stage4out_c &s4o; + + private: + /* Unlike programs that are mapped onto C++ classes, Function Blocks are mapped onto a data structure type + * and a separate function conatining the code. This function is passed a pointer to an instance of the data + * structure. This means that the code inside the functions must insert a pointer to the data structure whenever + * it wishes to access a Function Block variable. + * The variable_prefix_ variable will contain the correct string which needs to be prefixed to all variable accesses. + * This string is set with the set_variable_prefix() member function. + */ + const char *variable_prefix_; + + + + public: + generate_cc_base_c(stage4out_c *s4o_ptr): s4o(*s4o_ptr) {variable_prefix_ = NULL;} + ~generate_cc_base_c(void) {} + + void set_variable_prefix(const char *variable_prefix) {variable_prefix_ = variable_prefix;} + void print_variable_prefix(void) { + if (variable_prefix_ != NULL) + s4o.print(variable_prefix_); + } + + void *print_token(token_c *token, int offset = 0) { + return s4o.printupper((token->value)+offset); + } + + void *print_literal(symbol_c *type, symbol_c *value) { + s4o.print("("); + type->accept(*this); + s4o.print(")"); + value->accept(*this); + return NULL; + } + + void *print_striped_token(token_c *token, int offset = 0) { + std::string str = ""; + for (unsigned int i = offset; i < strlen(token->value); i++) + if (token->value[i] != '_') + str += token->value[i]; + return s4o.printupper(str); + } + + void *print_striped_binary_token(token_c *token, unsigned int offset = 0) { + /* convert the binary value to hexadecimal format... */ + unsigned char value, bit_mult; + unsigned int i; + int total_bits; + char str[2] = {'A', '\0'}; /* since the s4o object is not prepared to print out one character at a time... */ + + s4o.print("0x"); + + total_bits = 0; + for (i = offset; i < strlen(token->value); i++) + if (token->value[i] != '_') + total_bits++; + + value = 0; + bit_mult = (unsigned char)1 << (((total_bits+3)%4)+1); + for (i = offset; i < strlen(token->value); i++) { + if (token->value[i] != '_') { + bit_mult /= 2; + value += bit_mult * ((token->value[i] == '0')? 0:1); + if (bit_mult == 1) { + str[0] = (value <= 9)? (char)'0' + value : (char)'A' + value; + s4o.print(str); + bit_mult = 0x10; + value = 0; + } + } + } + + return NULL; + } + + void *print_list(list_c *list, + std::string pre_elem_str = "", + std::string inter_elem_str = "", + std::string post_elem_str = "", + visitor_c *visitor = NULL) { + if (visitor == NULL) visitor = this; + + if (list->n > 0) { +//std::cout << "generate_cc_base_c::print_list(n = " << list->n << ") 000\n"; + s4o.print(pre_elem_str); + list->elements[0]->accept(*visitor); + } + + for(int i = 1; i < list->n; i++) { +//std::cout << "generate_cc_base_c::print_list " << i << "\n"; + s4o.print(inter_elem_str); + list->elements[i]->accept(*visitor); + } + + if (list->n > 0) + s4o.print(post_elem_str); + + return NULL; + } + + + void *print_binary_expression(symbol_c *l_exp, + symbol_c *r_exp, + const char *operation) { + s4o.print("("); + l_exp->accept(*this); + s4o.print(operation); + r_exp->accept(*this); + s4o.print(")"); + return NULL; + } + + + void *print_unary_expression(symbol_c *exp, + const char *operation) { + s4o.print(operation); + s4o.print("("); + exp->accept(*this); + s4o.print(")"); + return NULL; + } + + +/***************************/ +/* 2.1.6 - Pragmas */ +/***************************/ + /* Do not use print_token() as it will change everything into uppercase */ + void *visit(pragma_c *symbol) {return s4o.print(symbol->value);} + + +/***************************/ +/* B 0 - Programming Model */ +/***************************/ + /* leave for derived classes... */ + + + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ + void *visit(identifier_c *symbol) {return print_token(symbol);} + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + /* originally empty... */ + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ + void *visit(real_c *symbol) {return print_striped_token(symbol);} + void *visit(integer_c *symbol) {return print_striped_token(symbol);} + void *visit(binary_integer_c *symbol) {return print_striped_binary_token(symbol, 2);} + void *visit(octal_integer_c *symbol) {s4o.print("0"); return print_striped_token(symbol, 2);} + void *visit(hex_integer_c *symbol) {s4o.print("0x"); return print_striped_token(symbol, 3);} + + void *visit(numeric_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} + void *visit(integer_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} + void *visit(real_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} + void *visit(bit_string_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} + void *visit(boolean_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} + + /* helper class for boolean_literal_c */ + void *visit(boolean_true_c *symbol) {s4o.print("TRUE"); return NULL;} + void *visit(boolean_false_c *symbol) {s4o.print("FALSE"); return NULL;} + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ + void *visit(double_byte_character_string_c *symbol) { + // TO DO ... + ERROR; + return print_token(symbol); + } + + void *visit(single_byte_character_string_c *symbol) { + std::string str = ""; + + str += '"'; + /* we ignore the first and last bytes, they will be the character ' */ + for (unsigned int i = 1; i < strlen(symbol->value) - 1; i++) { + char c = symbol->value[i]; + if ((c == '\\') || (c == '"')) + {str += '\\'; str += c; continue;} + if (c != '$') + {str += c; continue;} + /* this should be safe, since the code has passed the syntax parser!! */ + c = symbol->value[++i]; + switch (c) { + case '$': + case '\'': + {str += c; continue;} + case 'L': + case 'l': + {str += "\x0A"; /* LF */; continue;} + case 'N': + case 'n': + {str += "\\x0A"; /* NL */; continue;} + case 'P': + case 'p': + {str += "\\f"; /* FF */; continue;} + case 'R': + case 'r': + {str += "\\r"; /* CR */; continue;} + case 'T': + case 't': + {str += "\\t"; /* tab */; continue;} + default: { + if (isxdigit(c)) { + /* this should be safe, since the code has passed the syntax parser!! */ + char c2 = symbol->value[++i]; + if (isxdigit(c2)) { + str += '\\'; str += 'x'; str += c; str += c2; + continue; + } + } + } + /* otherwise we have an invalid string!! */ + /* This should not have got through the syntax parser! */ + ERROR; + } /* switch() */ + } /* for() */ + + str += '"'; + + s4o.print(str); + return NULL; + } + + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ + +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +/* The following output is actually the parameters to the constructor of the TIME class! */ + +/* SYM_REF0(neg_time_c) */ +void *visit(neg_time_c *symbol) {s4o.print("-1"); /* negative time value */; return NULL;} + + +/* SYM_REF2(duration_c, neg, interval) */ +void *visit(duration_c *symbol) { + TRACE("duration_c"); + s4o.print("TIME("); + if (NULL == symbol->neg) + s4o.print("1"); /* positive time value */ + else + symbol->neg->accept(*this); /* this will print '-1' :-) */ + + s4o.print(", "); + + symbol->interval->accept(*this); + s4o.print(")"); + return NULL; +} + + +/* SYM_TOKEN(fixed_point_c) */ +void *visit(fixed_point_c *symbol) {return print_token(symbol);} + + +/* SYM_REF2(days_c, days, hours) */ +void *visit(days_c *symbol) { + TRACE("days_c"); + if (NULL == symbol->hours) + s4o.print("0, 0, 0, 0"); /* milliseconds, seconds, minutes, hours */ + else + symbol->hours->accept(*this); + + s4o.print(", "); + + symbol->days->accept(*this); + return NULL; +} + + +/* SYM_REF2(hours_c, hours, minutes) */ +void *visit(hours_c *symbol) { + TRACE("hours_c"); + if (NULL == symbol->minutes) + s4o.print("0, 0, 0"); /* milliseconds, seconds, minutes */ + else + symbol->minutes->accept(*this); + + s4o.print(", "); + + symbol->hours->accept(*this); + return NULL; +} + + +/* SYM_REF2(minutes_c, minutes, seconds) */ +void *visit(minutes_c *symbol) { + TRACE("minutes_c"); + if (NULL == symbol->seconds) + s4o.print("0, 0"); /* milliseconds, seconds */ + else + symbol->seconds->accept(*this); + + s4o.print(", "); + + symbol->minutes->accept(*this); + return NULL; +} + + +/* SYM_REF2(seconds_c, seconds, milliseconds) */ +void *visit(seconds_c *symbol) { + TRACE("seconds_c"); + if (NULL == symbol->milliseconds) + s4o.print("0"); /* milliseconds */ + else + symbol->milliseconds->accept(*this); + + s4o.print(", "); + + symbol->seconds->accept(*this); + return NULL; +} + + +/* SYM_REF2(milliseconds_c, milliseconds, unused) */ +void *visit(milliseconds_c *symbol) { + TRACE("milliseconds_c"); + symbol->milliseconds->accept(*this); + return NULL; +} + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ + +/* SYM_REF2(time_of_day_c, daytime, unused) */ +void *visit(time_of_day_c *symbol) { + TRACE("time_of_day_c"); + s4o.print("TOD("); + symbol->daytime->accept(*this); + s4o.print(")"); + return NULL; +} + + +/* SYM_REF4(daytime_c, day_hour, day_minute, day_second, unused) */ +void *visit(daytime_c *symbol) { + TRACE("daytime_c"); + symbol->day_second->accept(*this); + s4o.print(", "); + symbol->day_minute->accept(*this); + s4o.print(", "); + symbol->day_hour->accept(*this); + return NULL; +} + + +/* SYM_REF2(date_c, date_literal, unused) */ +void *visit(date_c *symbol) { + TRACE("date_c"); + s4o.print("DATE("); + symbol->date_literal->accept(*this); + s4o.print(")"); + return NULL; +} + + +/* SYM_REF4(date_literal_c, year, month, day, unused) */ +void *visit(date_literal_c *symbol) { + TRACE("date_literal_c"); + symbol->day->accept(*this); + s4o.print(", "); + symbol->month->accept(*this); + s4o.print(", "); + symbol->year->accept(*this); + return NULL; +} + + +/* SYM_REF2(date_and_time_c, date_literal, daytime) */ +void *visit(date_and_time_c *symbol) { + TRACE("date_and_time_c"); + s4o.print("DT("); + symbol->daytime->accept(*this); + s4o.print(", "); + symbol->date_literal->accept(*this); + s4o.print(")"); + return NULL; +} + + +/**********************/ +/* B.1.3 - Data types */ +/**********************/ +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ + void *visit(time_type_name_c *symbol) {s4o.print("TIME"); return NULL;} + void *visit(bool_type_name_c *symbol) {s4o.print("BOOL"); return NULL;} + void *visit(sint_type_name_c *symbol) {s4o.print("SINT"); return NULL;} + void *visit(int_type_name_c *symbol) {s4o.print("INT"); return NULL;} + void *visit(dint_type_name_c *symbol) {s4o.print("DINT"); return NULL;} + void *visit(lint_type_name_c *symbol) {s4o.print("LINT"); return NULL;} + void *visit(usint_type_name_c *symbol) {s4o.print("USINT"); return NULL;} + void *visit(uint_type_name_c *symbol) {s4o.print("UINT"); return NULL;} + void *visit(udint_type_name_c *symbol) {s4o.print("UDINT"); return NULL;} + void *visit(ulint_type_name_c *symbol) {s4o.print("ULINT"); return NULL;} + void *visit(real_type_name_c *symbol) {s4o.print("REAL"); return NULL;} + void *visit(lreal_type_name_c *symbol) {s4o.print("LREAL"); return NULL;} + void *visit(date_type_name_c *symbol) {s4o.print("DATE"); return NULL;} + void *visit(tod_type_name_c *symbol) {s4o.print("TOD"); return NULL;} + void *visit(dt_type_name_c *symbol) {s4o.print("DT"); return NULL;} + void *visit(byte_type_name_c *symbol) {s4o.print("BYTE"); return NULL;} + void *visit(word_type_name_c *symbol) {s4o.print("WORD"); return NULL;} + void *visit(lword_type_name_c *symbol) {s4o.print("LWORD"); return NULL;} + void *visit(dword_type_name_c *symbol) {s4o.print("DWORD"); return NULL;} + void *visit(string_type_name_c *symbol) {s4o.print("STRING"); return NULL;} + void *visit(wstring_type_name_c *symbol) {s4o.print("WSTRING"); return NULL;} + +/********************************/ +/* B.1.3.2 - Generic data types */ +/********************************/ + /* originally empty... */ + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + /* leave for derived classes... */ + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +void *visit(symbolic_variable_c *symbol) { + TRACE("symbolic_variable_c"); + this->print_variable_prefix(); + symbol->var_name->accept(*this); + return NULL; +} + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +void *visit(direct_variable_c *symbol) { + TRACE("direct_variable_c"); + /* Do not use print_token() as it will change everything into uppercase */ + return s4o.print(symbol->value); +} + + + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +#if 0 +/* subscripted_variable '[' subscript_list ']' */ +SYM_REF2(array_variable_c, subscripted_variable, subscript_list) + +/* subscript_list ',' subscript */ +SYM_LIST(subscript_list_c) +#endif + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a structured variable! + * Code handling a structured_variable_c must take + * this into account! + */ +// SYM_REF2(structured_variable_c, record_variable, field_selector) +void *visit(structured_variable_c *symbol) { + TRACE("structured_variable_c"); + + symbol->record_variable->accept(*this); + s4o.print("."); + symbol->field_selector->accept(*this); + return NULL; +} + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + /* leave for derived classes... */ + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ + /* leave for derived classes... */ + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ + /* leave for derived classes... */ + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ + /* leave for derived classes... */ + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ + /* leave for derived classes... */ + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + /* leave for derived classes... */ + +/*******************/ +/* B 2.2 Operators */ +/*******************/ + /* leave for derived classes... */ + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ + /* leave for derived classes... */ + +/********************/ +/* B 3.2 Statements */ +/********************/ + /* leave for derived classes... */ + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ + /* leave for derived classes... */ + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ + /* leave for derived classes... */ + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ + /* leave for derived classes... */ + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ + /* leave for derived classes... */ + +}; /* class generate_cc_basic_c */ + + + + + + + + + + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc_configbody.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc_configbody.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,211 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Build the body of the main function (currently named run() ) + * of the class onto which is mapped a specific CONFIGURATION. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + + + +// #include /* required for NULL */ +/* +#include +#include + +#include "../../util/symtable.hh" + +#include "../stage4.hh" +*/ + + + + + +class generate_cc_configbody_c: public generate_cc_base_c { + + public: + generate_cc_configbody_c(stage4out_c *s4o_ptr) + : generate_cc_base_c(s4o_ptr) { + current_resource_name = NULL; + } + + virtual ~generate_cc_configbody_c(void) {} + + + private: + /* The name of the resource curretnly being processed... */ + symbol_c *current_resource_name; + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ + + +public: +/* +CONFIGURATION configuration_name + optional_global_var_declarations + (resource_declaration_list | single_resource_declaration) + optional_access_declarations + optional_instance_specific_initializations +END_CONFIGURATION +*/ +/* +SYM_REF6(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, unused) +*/ +void *visit(configuration_declaration_c *symbol) { + TRACE("configuration_declaration_c"); + + symbol->resource_declarations->accept(*this); + return NULL; +} + + + +/* helper symbol for configuration_declaration */ +// SYM_LIST(resource_declaration_list_c) +void *visit(resource_declaration_list_c *symbol) { + TRACE("resource_declaration_list_c"); + + return print_list(symbol); +} + +/* +RESOURCE resource_name ON resource_type_name + optional_global_var_declarations + single_resource_declaration +END_RESOURCE +*/ +// SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration) +void *visit(resource_declaration_c *symbol) { + TRACE("resource_declaration_c"); + + current_resource_name = symbol->resource_name; + symbol->resource_declaration->accept(*this); + current_resource_name = NULL; + return NULL; +} + + + +/* task_configuration_list program_configuration_list */ +// SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) +void *visit(single_resource_declaration_c *symbol) { + TRACE("single_resource_declaration_c"); + + symbol->program_configuration_list->accept(*this); + return NULL; +} + + + +/* helper symbol for single_resource_declaration */ +//SYM_LIST(task_configuration_list_c) + + +/* helper symbol for single_resource_declaration */ +//SYM_LIST(program_configuration_list_c) +void *visit(program_configuration_list_c *symbol) { + TRACE("program_configuration_list_c"); + + return print_list(symbol); +} + + +/* helper symbol for + * - access_path + * - instance_specific_init + */ +//SYM_LIST(any_fb_name_list_c) + +/* [resource_name '.'] global_var_name ['.' structure_element_name] */ +//SYM_REF4(global_var_reference_c, resource_name, global_var_name, structure_element_name, unused) + +/* prev_declared_program_name '.' symbolic_variable */ +//SYM_REF2(program_output_reference_c, program_name, symbolic_variable) + +/* TASK task_name task_initialization */ +//SYM_REF2(task_configuration_c, task_name, task_initialization) + +/* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ +//SYM_REF4(task_initialization_c, single_data_source, interval_data_source, priority_data_source, unused) + +/* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ +//SYM_REF6(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, unused) +void *visit(program_configuration_c *symbol) { + TRACE("program_configuration_c"); + + s4o.print(s4o.indent_spaces); + if (NULL != current_resource_name) { + current_resource_name->accept(*this); + s4o.print("."); + } + symbol->program_name->accept(*this); + s4o.print(".f();\n"); + return NULL; +} + +/* prog_conf_elements ',' prog_conf_element */ +//SYM_LIST(prog_conf_elements_c) + +/* fb_name WITH task_name */ +//SYM_REF2(fb_task_c, fb_name, task_name) +// TODO... + +/* any_symbolic_variable ASSIGN prog_data_source */ +//SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) + +/* any_symbolic_variable SENDTO data_sink */ +//SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, prog_data_source) + +/* VAR_CONFIG instance_specific_init_list END_VAR */ +//SYM_REF2(instance_specific_initializations_c, instance_specific_init_list, unused) + +/* helper symbol for instance_specific_initializations */ +//SYM_LIST(instance_specific_init_list_c) + +/* resource_name '.' program_name '.' {fb_name '.'} + ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization)) +*/ +//SYM_REF6(instance_specific_init_c, resource_name, program_name, any_fb_name_list, variable_name, location, initialization) + +/* helper symbol for instance_specific_init */ +/* function_block_type_name ':=' structure_initialization */ +//SYM_REF2(fb_initialization_c, function_block_type_name, structure_initialization) + + + +}; /* generate_cc_configbody_c */ + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc_il.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc_il.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,1283 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Conversion of il statements (i.e. IL code). + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +/* Returns the data type of an il_operand. + * + * Note that the il_operand may be a variable, in which case + * we return the type of the variable instance. + * The il_operand may also be a constant, in which case + * we return the data type of that constant. + * + * The variable instance may be a member of a structured variable, + * or an element in an array, or any combination of the two. + * + * The class constructor must be given the search scope + * (function, function block or program within which + * the possible il_operand variable instance was declared). + */ +class search_il_operand_type_c { + + private: + search_varfb_instance_type_c search_varfb_instance_type; + search_constant_type_c search_constant_type; + + public: + search_il_operand_type_c(symbol_c *search_scope): search_varfb_instance_type(search_scope) {} + + public: + symbol_c *get_type(symbol_c *il_operand) { + symbol_c *res; + + /* We first assume that it is a constant... */ + res = search_constant_type.get_type(il_operand); + if (res != NULL) return res; + + /* Nope, now we assume it is a variable, and determine its type... */ + res = search_varfb_instance_type.get_type(il_operand); + if (NULL != res) return res; + + /* not found */ + return NULL; + } +}; + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + +/* A new class to ouput the il default variable to c++ code + * We use this class, inheriting from symbol_c, so it may be used + * as any other symbol_c object in the intermediate parse tree, + * more specifically, so it can be used as any other il operand. + * This makes the rest of the code much easier... + * + * Nevertheless, the basic visitor class visitor_c does not know + * how to visit this new il_default_variable_c class, so we have + * to extend that too. + * In reality extending the basic symbols doesn't quite work out + * as cleanly as desired (we need to use dynamic_cast in the + * accept method of the il_default_variable_c), but it is cleaner + * than the alternative... + */ +class il_default_variable_c; + +/* This visitor class is not really required, we could place the + * visit() method directly in genertae_cc_il_c, but doing it in + * a seperate class makes the architecture more evident... + */ +class il_default_variable_visitor_c { + public: + virtual void *visit(il_default_variable_c *symbol) = 0; + + virtual ~il_default_variable_visitor_c(void) {return;} +}; + + +/* A class to print out to the resulting C++ code + * the IL default variable name. + * + * It includes a reference to its name, + * and the data type of the data currently stored + * in this C++ variable... This is required because the + * C++ variable is a union, and we must know which member + * of the union top reference!! + * + * Note that we also need to keep track of the data type of + * the value currently being stored in the default variable. + * This is required so we can process parenthesis, + * + * e.g. : + * LD var1 + * AND ( + * LD var2 + * OR var3 + * ) + * + * Note that we only execute the 'AND (' operation when we come across + * the ')', i.e. once we have evaluated the result of the + * instructions inside the parenthesis. + * When we do execute the 'AND (' operation, we need to know the data type + * of the operand, which in this case is the result of the evaluation of the + * instruction list inside the parenthesis. We can only know this if we + * keep track of the data type currently stored in the default variable! + * + * We use the current_type inside the generate_cc_il::default_variable_name variable + * to track this! + */ +class il_default_variable_c: public symbol_c { + public: + symbol_c *var_name; /* in principle, this should point to an indentifier_c */ + symbol_c *current_type; + + public: + il_default_variable_c(const char *var_name_str, symbol_c *current_type); + virtual void *accept(visitor_c &visitor); +}; + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + +class generate_cc_il_c: public generate_cc_typedecl_c, il_default_variable_visitor_c { + + private: + /* When compiling il code, it becomes necessary to determine the + * data type of il operands. To do this, we must first find the + * il operand's declaration, within the scope of the function block + * or function currently being processed. + * The following object does just that... + * This object instance will then later be called while the + * remaining il code is being handled. + */ + search_il_operand_type_c *search_il_operand_type; + + /* The initial value that should be given to the IL default variable + * imediately after a parenthesis is opened. + * This variable is only used to pass data from the + * il_expression_c visitor to the simple_instr_list_c visitor. + * + * e.g.: + * LD var1 + * AND ( var2 + * OR var3 + * ) + * + * In the above code sample, the line 'AND ( var2' constitutes + * an il_expression_c, where var2 should be loaded into the + * il default variable before continuing with the expression + * inside the parenthesis. + * Unfortunately, only the simple_instr_list_c may do the + * initial laoding of the var2 bariable following the parenthesis, + * so the il_expression_c visitor will have to pass 'var2' as a + * parameter to the simple_instr_list_c visitor. + * Ergo, the existance of the following parameter...! + */ + symbol_c *il_default_variable_init_value; + + /* Operand to the IL operation currently being processed... */ + /* These variables are used to pass data from the + * il_simple_operation_c and il_expression_c visitors + * to the il operator visitors (i.e. LD_operator_c, + * LDN_operator_c, ST_operator_c, STN_operator_c, ...) + */ + symbol_c *current_operand; + symbol_c *current_operand_type; + + /* Label to which the current IL jump operation should jump to... */ + /* This variable is used to pass data from the + * il_jump_operation_c visitor + * to the il jump operator visitors (i.e. JMP_operator_c, + * JMPC_operator_c, JMPCN_operator_c, ...) + */ + symbol_c *jump_label; + + /* The result of the comparison IL operations (GT, EQ, LT, ...) + * is a boolean variable. + * This class keeps track of the current data type stored in the + * il default variable. This is usually done by keeping a reference + * to the data type of the last operand. Nevertheless, in the case of + * the comparison IL operators, the data type of the result (a boolean) + * is not the data type of the operand. We therefore need an object + * of the boolean data type to keep as a reference of the current + * data type. + * The following object is it... + */ + bool_type_name_c bool_type; + + /* the data type of the IL default variable... */ + #define IL_DEFVAR_T VAR_LEADER "IL_DEFVAR_T" + /* The name of the IL default variable... */ + #define IL_DEFVAR VAR_LEADER "IL_DEFVAR" + /* The name of the variable used to pass the result of a + * parenthesised instruction list to the immediately preceding + * scope ... + */ + #define IL_DEFVAR_BACK VAR_LEADER "IL_DEFVAR_BACK" + il_default_variable_c default_variable_name; + il_default_variable_c default_variable_back_name; + + /* Some function calls in the body of functions or function blocks + * may leave some parameters to their default values, and + * ignore some output parameters of the function being called. + * Our conversion of ST functions to C++ does not contemplate that, + * i.e. each called function must get all it's input and output + * parameters set correctly. + * For input parameters we merely need to call the function with + * the apropriate default value, but for output parameters + * we must create temporary variables to hold the output value. + * + * We declare all the temporary output variables at the begining of + * the body of each function or function block, and use them as + * in function calls later on as they become necessary... + * Note that we cannot create these variables just before a function + * call, as the function call itself may be integrated within an + * expression, or another function call! + * + * The variables are declared in the exact same order in which they + * will be used later on during the function calls, which allows us + * to simply re-create the name that was used for the temporary variable + * instead of keeping it in some list. + * The names are recreated by the temp_var_name_factory, after reset() + * has been called! + * + * This function will genertae code similar to... + * + * INT __TMP_0 = 23; + * REAL __TMP_1 = 45.5; + * ... + */ + temp_var_name_c temp_var_name_factory; + + /* When calling a function block, we must first find it's type, + * by searching through the declarations of the variables currently + * in scope. + * This class does just that... + * A new class is instantiated whenever we begin generating the code + * for a function block type declaration, or a program declaration. + * This object instance will then later be called while the + * function block's or the program's body is being handled. + * + * Note that functions cannot contain calls to function blocks, + * so we do not create an object instance when handling + * a function declaration. + */ + search_fb_instance_decl_c *search_fb_instance_decl; + + + public: + generate_cc_il_c(stage4out_c *s4o_ptr, symbol_c *scope, const char *variable_prefix = NULL) + : generate_cc_typedecl_c(s4o_ptr), + default_variable_name(IL_DEFVAR, NULL), + default_variable_back_name(IL_DEFVAR_BACK, NULL) + { + search_il_operand_type = new search_il_operand_type_c(scope); + search_fb_instance_decl = new search_fb_instance_decl_c(scope); + current_operand = NULL; + current_operand_type = NULL; + il_default_variable_init_value = NULL; + this->set_variable_prefix(variable_prefix); + } + + virtual ~generate_cc_il_c(void) { + delete search_fb_instance_decl; + delete search_il_operand_type; + } + + void generate(instruction_list_c *il) { + generate_cc_tempvardecl_c generate_cc_tempvardecl(&s4o); + generate_cc_tempvardecl.generate(il, &temp_var_name_factory); + il->accept(*this); + } + + private: + /* A helper function... */ + bool is_bool_type(symbol_c *type_symbol) { + return (NULL != dynamic_cast(type_symbol)); + } + + /* A helper function... */ + void *XXX_operator(symbol_c *lo, const char *op, symbol_c *ro) { + if ((NULL == lo) || (NULL == ro)) ERROR; + if (NULL == op) ERROR; + + lo->accept(*this); + s4o.print(op); + ro->accept(*this); + return NULL; + } + + /* A helper function... */ + void *XXX_CAL_operator(const char *param_name, symbol_c *fb_name) { + if (NULL == fb_name) ERROR; + symbolic_variable_c *sv = dynamic_cast(fb_name); + if (NULL == sv) ERROR; + identifier_c *id = dynamic_cast(sv->var_name); + if (NULL == id) ERROR; + + identifier_c param(param_name); + + //SYM_REF4(il_param_assignment_c, il_assign_operator, il_operand, simple_instr_list, unused) + il_param_assignment_c il_param_assignment(¶m, &this->default_variable_name, NULL, NULL); + // SYM_LIST(il_param_list_c) + il_param_list_c il_param_list; + il_param_list.add_element(&il_param_assignment); + CAL_operator_c CAL_operator; + // SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list) + il_fb_call_c il_fb_call(&CAL_operator, id, NULL, &il_param_list); + + il_fb_call.accept(*this); + return NULL; + } + + /* A helper function... */ + void *CMP_operator(symbol_c *o, const char *operation) { + if (NULL == o) ERROR; + if (NULL == this->default_variable_name.current_type) ERROR; + + symbol_c *backup = this->default_variable_name.current_type; + this->default_variable_name.current_type = &(this->bool_type); + this->default_variable_name.accept(*this); + this->default_variable_name.current_type = backup; + + s4o.print(" = ("); + this->default_variable_name.accept(*this); + s4o.print(operation); + o->accept(*this); + s4o.print(")"); + + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = &(this->bool_type); + return NULL; + } + + + /* A helper function... */ + void C_modifier(void) { + if (NULL == this->default_variable_name.current_type) ERROR; + + s4o.print("if ("); + this->default_variable_name.accept(*this); + s4o.print(") "); + } + + /* A helper function... */ + void CN_modifier(void) { + if (NULL == this->default_variable_name.current_type) ERROR; + + s4o.print("if (!"); + this->default_variable_name.accept(*this); + s4o.print(") "); + } + + +public: +void *visit(il_default_variable_c *symbol) { + //s4o.print("il_default_variable_c VISITOR!!\n"); + symbol->var_name->accept(*this); + if (NULL != symbol->current_type) { + s4o.print("."); + symbol->current_type->accept(*this); + s4o.print("var"); + } + return NULL; +} + + +private: +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ + +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + +/* please see the comment before the RET_operator_c visitor for details... */ +#define END_LABEL VAR_LEADER "end" + +/*| instruction_list il_instruction */ +void *visit(instruction_list_c *symbol) { + /* Declare the backup to the default variable, that will store the result + * of the IL operations executed inside a parenthesis... + */ + s4o.print(s4o.indent_spaces); + s4o.print(IL_DEFVAR_T); + s4o.print(" "); + this->default_variable_back_name.accept(*this); + s4o.print(";\n"); + + /* Declare the default variable, that will store the result of the IL operations... */ + s4o.print(s4o.indent_spaces); + s4o.print(IL_DEFVAR_T); + s4o.print(" "); + this->default_variable_name.accept(*this); + s4o.print(";\n\n"); + + print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); + + /* write the label marking the end of the code block */ + /* please see the comment before the RET_operator_c visitor for details... */ + s4o.print("\n"); + s4o.print(s4o.indent_spaces); + s4o.print(END_LABEL); + s4o.print(":\n"); + s4o.indent_right(); + /* since every label must be followed by at least one statement, and + * only the functions will introduce the return statement after this label, + * function blocks written in IL would result in invalid C++ code. + * To work around this we introduce the equivalent of a 'nop' operation + * to humour the compiler... + */ + s4o.print(s4o.indent_spaces); + s4o.print("/* to humour the compiler, we insert a nop */\n"); + s4o.print(s4o.indent_spaces); + this->default_variable_name.accept(*this); + s4o.print(" = "); + this->default_variable_name.accept(*this); + s4o.print(";\n"); + s4o.indent_left(); + + return NULL; +} + + +/* | label ':' [il_incomplete_instruction] eol_list */ +// SYM_REF2(il_instruction_c, label, il_instruction) +void *visit(il_instruction_c *symbol) { + if (NULL != symbol->label) { + symbol->label->accept(*this); + s4o.print(":\n"); + s4o.print(s4o.indent_spaces); + } + symbol->il_instruction->accept(*this); + return NULL; +} + +/* | il_simple_operator [il_operand] */ +//SYM_REF2(il_simple_operation_c, il_simple_operator, il_operand) +void *visit(il_simple_operation_c *symbol) { + this->current_operand = symbol->il_operand; + if (NULL == this->current_operand) { + this->current_operand_type = NULL; + } else { + this->current_operand_type = search_il_operand_type->get_type(this->current_operand); + if (NULL == this->current_operand_type) ERROR; + } + + symbol->il_simple_operator->accept(*this); + + this->current_operand = NULL; + this->current_operand_type = NULL; + return NULL; +} + + +/* | function_name [il_operand_list] */ +// SYM_REF2(il_function_call_c, function_name, il_operand_list) +void *visit(il_function_call_c *symbol) { + function_declaration_c *f_decl = function_symtable.find_value(symbol->function_name); + + if (f_decl == function_symtable.end_value()) + /* should never occur. The function being called MUST be in the symtable... */ + ERROR; + + /* determine the base data type returned by the function being called... */ + search_base_type_c search_base_type; + symbol_c *return_data_type = (symbol_c *)f_decl->type_name->accept(search_base_type); + symbol_c *param_data_type = default_variable_name.current_type; + if (NULL == return_data_type) ERROR; + + default_variable_name.current_type = return_data_type; + this->default_variable_name.accept(*this); + default_variable_name.current_type = param_data_type; + s4o.print(" = "); + + symbol->function_name->accept(*this); + s4o.print("("); + + /* loop through each function parameter, find the value we should pass + * to it, and then output the c equivalent... + */ + + function_param_iterator_c fp_iterator(f_decl); + identifier_c *param_name; + function_call_param_iterator_c function_call_param_iterator(symbol); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + if (i != 1) + s4o.print(", "); + + symbol_c *param_type = fp_iterator.param_type(); + if (param_type == NULL) ERROR; + + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + + symbol_c *param_value = NULL; + + /* if it is the first parameter, semantics specifies that we should + * get the value off the IL default variable! + */ + if (1 == i) + param_value = &this->default_variable_name; + + /* Get the value from a foo( = ) style call */ + /* NOTE: the following line of code is not required in this case, but it doesn't + * harm to leave it in, as in the case of a non-formal syntax function call, + * it will always return NULL. + * We leave it in in case we later decide to merge this part of the code together + * with the function calling code in generate_cc_st_c, which does require + * the following line... + */ + if (param_value == NULL) + param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + switch (param_direction) { + case function_param_iterator_c::direction_in: + if (param_value == NULL) { + /* No value given for parameter, so we must use the default... */ + /* First check whether default value specified in function declaration...*/ + param_value = fp_iterator.default_value(); + } + if (param_value == NULL) { + /* If not, get the default value of this variable's type */ + param_value = (symbol_c *)param_type->accept(*type_initial_value_c::instance()); + } + if (param_value == NULL) ERROR; + param_value->accept(*this); + break; + case function_param_iterator_c::direction_out: + case function_param_iterator_c::direction_inout: + if (param_value == NULL) { + /* no parameter value given, so we pass a previously declared temporary variable. */ + std::string *temp_var_name = temp_var_name_factory.new_name(); + s4o.print(*temp_var_name); + delete temp_var_name; + } else { + param_value->accept(*this); + } + break; + case function_param_iterator_c::direction_extref: + /* TODO! */ + ERROR; + break; + } /* switch */ + } /* for(...) */ + + s4o.print(")"); + + /* the data type returned by the function, and stored in the il default variable... */ + default_variable_name.current_type = return_data_type; + + return NULL; +} + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +//SYM_REF4(il_expression_c, il_expr_operator, il_operand, simple_instr_list, unused) +void *visit(il_expression_c *symbol) { + /* We will be recursevely interpreting an instruction list, + * so we store a backup of the data type of the value currently stored + * in the default variable, and set the current data type to NULL + */ + symbol_c *old_current_default_variable_data_type = this->default_variable_name.current_type; + this->default_variable_name.current_type = NULL; + + /* Pass the symbol->il_operand to the simple_instr_list visitor + * using the il_default_variable_init_value parameter... + * Note that the simple_instr_list_c visitor will set this parameter + * to NULL as soon as it does not require it any longer, + * so we don't do it here again after the + * symbol->simple_instr_list->accept(*this); + * returns... + */ + this->il_default_variable_init_value = symbol->il_operand; + + /* Now do the parenthesised instructions... */ + /* NOTE: the following code line will get the variable + * this->default_variable_name.current_type updated! + */ + symbol->simple_instr_list->accept(*this); + + /* Now do the operation, using the previous result! */ + /* NOTE: The result of the previous instruction list will be stored + * in a variable named IL_DEFVAR_BACK. This is done in the visitor + * to instruction_list_c objects... + */ + this->current_operand = &(this->default_variable_back_name); + this->current_operand_type = this->default_variable_back_name.current_type; + + this->default_variable_name.current_type = old_current_default_variable_data_type; + if (NULL == this->current_operand_type) ERROR; + + symbol->il_expr_operator->accept(*this); + + this->current_operand = NULL; + this->current_operand_type = NULL; + this->default_variable_back_name.current_type = NULL; + return NULL; +} + +/* il_jump_operator label */ +// SYM_REF2(il_jump_operation_c, il_jump_operator, label) +void *visit(il_jump_operation_c *symbol) { + /* Pass the symbol->label to the il_jump_operation visitor + * using the jump_label parameter... + */ + this->jump_label = symbol->label; + symbol->il_jump_operator->accept(*this); + this->jump_label = NULL; + + return NULL; +} + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +// SYM_REF4(il_fb_call_c, il_call_operator, fb_name, il_operand_list, il_param_list) +void *visit(il_fb_call_c *symbol) { + symbol->il_call_operator->accept(*this); + s4o.print("{\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + + /* first figure out what is the name of the function block type of the function block being called... */ + symbol_c *function_block_type_name = this->search_fb_instance_decl->get_type_name(symbol->fb_name); + /* should never occur. The function block instance MUST have been declared... */ + if (function_block_type_name == NULL) ERROR; + + /* Now find the declaration of the function block type being called... */ + function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(function_block_type_name); + /* should never occur. The function block type being called MUST be in the symtable... */ + if (fb_decl == function_block_type_symtable.end_value()) ERROR; + + /* loop through each function block parameter, find the value we should pass + * to it, and then output the c equivalent... + */ + function_param_iterator_c fp_iterator(fb_decl); + identifier_c *param_name; + function_call_param_iterator_c function_call_param_iterator(symbol); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* Get the value from a foo( = ) style call */ + symbol_c *param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + /* now output the value assignment */ + if (param_value != NULL) + if ((param_direction == function_param_iterator_c::direction_in) || + (param_direction == function_param_iterator_c::direction_inout)) { + symbol->fb_name->accept(*this); + s4o.print("."); + param_name->accept(*this); + s4o.print(" = "); + param_value->accept(*this); + s4o.print(";\n" + s4o.indent_spaces); + } + } /* for(...) */ + + /* now call the function... */ + function_block_type_name->accept(*this); + s4o.print(FB_FUNCTION_SUFFIX); + s4o.print("(&"); + symbol->fb_name->accept(*this); + s4o.print(")"); + + /* loop through each function parameter, find the variable to which + * we should atribute the value of all output or inoutput parameters. + */ + fp_iterator.reset(); + function_call_param_iterator.reset(); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* Get the value from a foo( = ) style call */ + symbol_c *param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + /* now output the value assignment */ + if (param_value != NULL) + if ((param_direction == function_param_iterator_c::direction_out) || + (param_direction == function_param_iterator_c::direction_inout)) { + s4o.print(";\n"+ s4o.indent_spaces); + param_value->accept(*this); + s4o.print(" = "); + symbol->fb_name->accept(*this); + s4o.print("."); + param_name->accept(*this); + } + } /* for(...) */ + + s4o.print(";\n"); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); + s4o.print("}"); + + return NULL; +} + + + +/* | function_name '(' eol_list [il_param_list] ')' */ +// SYM_REF2(il_formal_funct_call_c, function_name, il_param_list) +void *visit(il_formal_funct_call_c *symbol) { + function_declaration_c *f_decl = function_symtable.find_value(symbol->function_name); + + if (f_decl == function_symtable.end_value()) + /* should never occur. The function being called MUST be in the symtable... */ + ERROR; + + symbol->function_name->accept(*this); + s4o.print("("); + + /* loop through each function parameter, find the value we should pass + * to it, and then output the c equivalent... + */ + + function_param_iterator_c fp_iterator(f_decl); + identifier_c *param_name; + function_call_param_iterator_c function_call_param_iterator(symbol); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + if (i != 1) + s4o.print(", "); + + symbol_c *param_type = fp_iterator.param_type(); + if (param_type == NULL) ERROR; + + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + + symbol_c *param_value = NULL; + + /* Get the value from a foo( = ) style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + /* NOTE: the following line of code is not required in this case, but it doesn't + * harm to leave it in, as in the case of a formal syntax function call, + * it will always return NULL. + * We leave it in in case we later decide to merge this part of the code together + * with the function calling code in generate_cc_st_c, which does require + * the following line... + */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + switch (param_direction) { + case function_param_iterator_c::direction_in: + if (param_value == NULL) { + /* No value given for parameter, so we must use the default... */ + /* First check whether default value specified in function declaration...*/ + param_value = fp_iterator.default_value(); + } + if (param_value == NULL) { + /* If not, get the default value of this variable's type */ + param_value = (symbol_c *)param_type->accept(*type_initial_value_c::instance()); + } + if (param_value == NULL) ERROR; + param_value->accept(*this); + break; + case function_param_iterator_c::direction_out: + case function_param_iterator_c::direction_inout: + if (param_value == NULL) { + /* no parameter value given, so we pass a previously declared temporary variable. */ + std::string *temp_var_name = temp_var_name_factory.new_name(); + s4o.print(*temp_var_name); + delete temp_var_name; + } else { + param_value->accept(*this); + } + break; + case function_param_iterator_c::direction_extref: + /* TODO! */ + ERROR; + break; + } /* switch */ + } /* for(...) */ + + // symbol->parameter_assignment->accept(*this); + s4o.print(")"); + return NULL; +} + + +/* | il_operand_list ',' il_operand */ +// SYM_LIST(il_operand_list_c) +void *visit(il_operand_list_c *symbol) {ERROR; return NULL;} // should never get called! + + +/* | simple_instr_list il_simple_instruction */ +// SYM_LIST(simple_instr_list_c) +void *visit(simple_instr_list_c *symbol) { + /* A simple_instr_list_c is used to store a list of il operations + * being done within parenthesis... + * + * e.g.: + * LD var1 + * AND ( var2 + * OR var3 + * OR var4 + * ) + * + * This will be converted to C++ by defining a new scope + * with a new il default variable, and executing the il operands + * within this new scope. + * At the end of the scope the result, i.e. the value currently stored + * in the il default variable is copied to the variable used to take this + * value to the outside scope... + * + * The above example will result in the following C++ code: + * {__IL_DEFVAR_T __IL_DEFVAR_BACK; + * __IL_DEFVAR_T __IL_DEFVAR; + * + * __IL_DEFVAR.INTvar = var1; + * { + * __IL_DEFVAR_T __IL_DEFVAR; + * + * __IL_DEFVAR.INTvar = var2; + * __IL_DEFVAR.INTvar |= var3; + * __IL_DEFVAR.INTvar |= var4; + * + * __IL_DEFVAR_BACK = __IL_DEFVAR; + * } + * __IL_DEFVAR.INTvar &= __IL_DEFVAR_BACK.INTvar; + * + * } + * + * The intial value of the il default variable (in the above + * example 'var2') is passed to this simple_instr_list_c visitor + * using the il_default_variable_init_value parameter. + * Since it is possible to have parenthesis inside other parenthesis + * recursively, we reset the il_default_variable_init_value to NULL + * as soon as we no longer require it, as it may be used once again + * in the line + * print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); + * + */ + + /* Declare the default variable, that will store the result of the IL operations... */ + s4o.print("{\n"); + s4o.indent_right(); + + s4o.print(s4o.indent_spaces); + s4o.print(IL_DEFVAR_T); + s4o.print(" "); + this->default_variable_name.accept(*this); + s4o.print(";\n\n"); + + /* Check whether we should initiliase the il default variable... */ + if (NULL != this->il_default_variable_init_value) { + /* Yes, we must... */ + /* We will do it by instatiating a LD operator, and having this + * same generate_cc_il_c class visiting it! + */ + LD_operator_c ld_oper; + il_simple_operation_c il_simple_oper(&ld_oper, this->il_default_variable_init_value); + + s4o.print(s4o.indent_spaces); + il_simple_oper.accept(*this); + s4o.print(";\n"); + } + + /* this parameter no longer required... */ + this->il_default_variable_init_value = NULL; + + print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); + + /* copy the result in the default variable to the variable + * used to pass the data out to the scope enclosing + * the current scope! + * + * We also need to update the data type currently stored within + * the variable used to pass the data to the outside scope... + */ + this->default_variable_back_name.current_type = this->default_variable_name.current_type; + s4o.print("\n"); + s4o.print(s4o.indent_spaces); + this->default_variable_back_name.accept(*this); + s4o.print(" = "); + this->default_variable_name.accept(*this); + s4o.print(";\n"); + + s4o.indent_left(); + s4o.print(s4o.indent_spaces); + s4o.print("}\n"); + s4o.print(s4o.indent_spaces); + return NULL; +} + +/* | il_initial_param_list il_param_instruction */ +// SYM_LIST(il_param_list_c) +void *visit(il_param_list_c *symbol) {ERROR; return NULL;} // should never get called! + +/* il_assign_operator il_operand + * | il_assign_operator '(' eol_list simple_instr_list ')' + */ +// SYM_REF4(il_param_assignment_c, il_assign_operator, il_operand, simple_instr_list, unused) +void *visit(il_param_assignment_c *symbol) {ERROR; return NULL;} // should never get called! + +/* il_assign_out_operator variable */ +// SYM_REF2(il_param_out_assignment_c, il_assign_out_operator, variable); +void *visit(il_param_out_assignment_c *symbol) {ERROR; return NULL;} // should never get called! + +/*******************/ +/* B 2.2 Operators */ +/*******************/ + +void *visit(LD_operator_c *symbol) { + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + XXX_operator(&(this->default_variable_name), " = ", this->current_operand); + return NULL; +} + +void *visit(LDN_operator_c *symbol) { + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + XXX_operator(&(this->default_variable_name), + is_bool_type(this->current_operand_type)?" = !":" = ~", + this->current_operand); + return NULL; +} + +void *visit(ST_operator_c *symbol) { + XXX_operator(this->current_operand, " = ",&(this->default_variable_name)); + /* the data type resulting from this operation is unchamged. */ + return NULL; +} + +void *visit(STN_operator_c *symbol) { + XXX_operator(this->current_operand, + is_bool_type(this->current_operand_type)?" = !":" = ~", + &(this->default_variable_name)); + /* the data type resulting from this operation is unchamged. */ + return NULL; +} + +void *visit(NOT_operator_c *symbol) { + if ((NULL != this->current_operand) || (NULL != this->current_operand_type)) ERROR; + XXX_operator(&(this->default_variable_name), + is_bool_type(this->default_variable_name.current_type)?" = !":" = ~", + &(this->default_variable_name)); + /* the data type resulting from this operation is unchanged. */ + return NULL; +} + +void *visit(S_operator_c *symbol) { + if ((NULL == this->current_operand) || (NULL == this->current_operand_type)) ERROR; + + C_modifier(); + this->current_operand->accept(*this); + s4o.print(is_bool_type(this->current_operand_type)?" = true":" = 1"); + /* the data type resulting from this operation is unchanged! */ + return NULL; +} + +void *visit(R_operator_c *symbol) { + if ((NULL == this->current_operand) || (NULL == this->current_operand_type)) ERROR; + + C_modifier(); + this->current_operand->accept(*this); + s4o.print(is_bool_type(this->current_operand_type)?" = false":" = 0"); + /* the data type resulting from this operation is unchanged! */ + return NULL; +} + +void *visit(S1_operator_c *symbol) {return XXX_CAL_operator("S1", this->current_operand);} +void *visit(R1_operator_c *symbol) {return XXX_CAL_operator("R1", this->current_operand);} +void *visit(CLK_operator_c *symbol) {return XXX_CAL_operator("CLK", this->current_operand);} +void *visit(CU_operator_c *symbol) {return XXX_CAL_operator("CU", this->current_operand);} +void *visit(CD_operator_c *symbol) {return XXX_CAL_operator("CD", this->current_operand);} +void *visit(PV_operator_c *symbol) {return XXX_CAL_operator("PV", this->current_operand);} +void *visit(IN_operator_c *symbol) {return XXX_CAL_operator("IN", this->current_operand);} +void *visit(PT_operator_c *symbol) {return XXX_CAL_operator("PT", this->current_operand);} + +void *visit(AND_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), " &= ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(OR_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), " |= ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(XOR_operator_c *symbol) { + // '^' is a bit by bit exclusive OR !! Also seems to work with boolean types! + XXX_operator(&(this->default_variable_name), " ^= ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(ANDN_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), + is_bool_type(this->current_operand_type)?" &= !":" &= ~", + this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(ORN_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), + is_bool_type(this->current_operand_type)?" |= !":" |= ~", + this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(XORN_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), + // bit by bit exclusive OR !! Also seems to work with boolean types! + is_bool_type(this->current_operand_type)?" ^= !":" ^= ~", + this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(ADD_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), " += ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(SUB_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), " -= ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(MUL_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), " *= ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(DIV_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), " /= ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(MOD_operator_c *symbol) { + XXX_operator(&(this->default_variable_name), " %= ", this->current_operand); + /* the data type resulting from this operation... */ + this->default_variable_name.current_type = this->current_operand_type; + return NULL; +} + +void *visit(GT_operator_c *symbol) { + return CMP_operator(this->current_operand, " > "); +} + +void *visit(GE_operator_c *symbol) { + return CMP_operator(this->current_operand, " >= "); +} + +void *visit(EQ_operator_c *symbol) { + return CMP_operator(this->current_operand, " == "); +} + +void *visit(LT_operator_c *symbol) { + return CMP_operator(this->current_operand, " < "); +} + +void *visit(LE_operator_c *symbol) { + return CMP_operator(this->current_operand, " <= "); +} + +void *visit(NE_operator_c *symbol) { + return CMP_operator(this->current_operand, " != "); +} + + +//SYM_REF0(CAL_operator_c) +// This method will be called from within the il_fb_call_c visitor method +void *visit(CAL_operator_c *symbol) {return NULL;} + +//SYM_REF0(CALC_operator_c) +// This method will be called from within the il_fb_call_c visitor method +void *visit(CALC_operator_c *symbol) {C_modifier(); return NULL;} + +//SYM_REF0(CALCN_operator_c) +// This method will be called from within the il_fb_call_c visitor method +void *visit(CALCN_operator_c *symbol) {CN_modifier(); return NULL;} + +/* NOTE: The semantics of the RET operator requires us to return a value + * if the IL code is inside a function, but simply return no value if + * the IL code is inside a function block or program! + * Nevertheless, it is the generate_cc_c class itself that + * introduces the 'reaturn ' into the c++ code at the end + * of every function. This class does not know whether the IL code + * is inside a function or a function block. + * We work around this by jumping to the end of the code, + * that will be marked by the END_LABEL label in the + * instruction_list_c visitor... + */ +// SYM_REF0(RET_operator_c) +void *visit(RET_operator_c *symbol) { + s4o.print("goto ");s4o.print(END_LABEL); + return NULL; +} + +// SYM_REF0(RETC_operator_c) +void *visit(RETC_operator_c *symbol) { + C_modifier(); + s4o.print("goto ");s4o.print(END_LABEL); + return NULL; +} + +// SYM_REF0(RETCN_operator_c) +void *visit(RETCN_operator_c *symbol) { + CN_modifier(); + s4o.print("goto ");s4o.print(END_LABEL); + return NULL; +} + +//SYM_REF0(JMP_operator_c) +void *visit(JMP_operator_c *symbol) { + if (NULL == this->jump_label) ERROR; + + s4o.print("goto "); + this->jump_label->accept(*this); + /* the data type resulting from this operation is unchanged! */ + return NULL; +} + +// SYM_REF0(JMPC_operator_c) +void *visit(JMPC_operator_c *symbol) { + if (NULL == this->jump_label) ERROR; + + C_modifier(); + s4o.print("goto "); + this->jump_label->accept(*this); + /* the data type resulting from this operation is unchanged! */ + return NULL; +} + +// SYM_REF0(JMPCN_operator_c) +void *visit(JMPCN_operator_c *symbol) { + if (NULL == this->jump_label) ERROR; + + CN_modifier(); + s4o.print("goto "); + this->jump_label->accept(*this); + /* the data type resulting from this operation is unchanged! */ + return NULL; +} + +#if 0 +/*| [NOT] any_identifier SENDTO */ +SYM_REF2(il_assign_out_operator_c, option, variable_name) +#endif + +}; /* generate_cc_il_c */ + + + + + + + + + +/* The implementation of the single visit() member function + * of il_default_variable_c. + * It can only come after the full declaration of + * generate_cc_il_c. Since we define and declare + * generate_cc_il_c simultaneously, it can only come + * after the definition... + */ +void *il_default_variable_c::accept(visitor_c &visitor) { + /* An ugly hack!! */ + /* This is required because we need to over-ride the base + * accept(visitor_c &) method of the class symbol_c, + * so this method may be called through a symbol_c * + * reference! + * + * But, the visitor_c does not include a visitor to + * an il_default_variable_c, which means that we couldn't + * simply call visitor.visit(this); + * + * We therefore need to use the dynamic_cast hack!! + * + * Note too that we can't cast a visitor_c to a + * il_default_variable_visitor_c, since they are not related. + * Nor may the il_default_variable_visitor_c inherit from + * visitor_c, because then generate_cc_il_c would contain + * two visitor_c base classes, one each through + * il_default_variable_visitor_c and generate_cc_type_c + * + * We could use virtual inheritance of the visitor_c, but it + * would probably create more problems than it is worth! + */ + generate_cc_il_c *v; + v = dynamic_cast(&visitor); + if (v == NULL) ERROR; + + return v->visit(this); +} + + + + +il_default_variable_c::il_default_variable_c(const char *var_name_str, symbol_c *current_type) { + if (NULL == var_name_str) ERROR; + /* Note: current_type may start off with NULL */ + + this->var_name = new identifier_c(var_name_str); + if (NULL == this->var_name) ERROR; + + this->current_type = current_type; +} diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc_st.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc_st.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,506 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Conversion of st statements (i.e. ST code). + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + + +class generate_cc_st_c: public generate_cc_typedecl_c { + + private: + /* When calling a function block, we must first find it's type, + * by searching through the declarations of the variables currently + * in scope. + * This class does just that... + * A new class is instantiated whenever we begin generating the code + * for a function block type declaration, or a program declaration. + * This object instance will then later be called while the + * function block's or the program's body is being handled. + * + * Note that functions cannot contain calls to function blocks, + * so we do not create an object instance when handling + * a function declaration. + */ + search_fb_instance_decl_c *search_fb_instance_decl; + + + public: + generate_cc_st_c(stage4out_c *s4o_ptr, symbol_c *scope, const char *variable_prefix = NULL) + : generate_cc_typedecl_c(s4o_ptr) { + search_fb_instance_decl = new search_fb_instance_decl_c(scope); + this->set_variable_prefix(variable_prefix); + } + + virtual ~generate_cc_st_c(void) { + delete search_fb_instance_decl; + } + + + private: + /* Some function calls in the body of functions or function blocks + * may leave some parameters to their default values, and + * ignore some output parameters of the function being called. + * Our conversion of ST functions to C++ does not contemplate that, + * i.e. each called function must get all it's input and output + * parameters set correctly. + * For input parameters we merely need to call the function with + * the apropriate default value, but for output parameters + * we must create temporary variables to hold the output value. + * + * We declare all the temporary output variables at the begining of + * the body of each function or function block, and use them as + * in function calls later on as they become necessary... + * Note that we cannot create these variables just before a function + * call, as the function call itself may be integrated within an + * expression, or another function call! + * + * The variables are declared in the exact same order in which they + * will be used later on during the function calls, which allows us + * to simply re-create the name that was used for the temporary variable + * instead of keeping it in some list. + * The names are recreated by the temp_var_name_factory, after reset() + * has been called! + * + * This function will genertae code similar to... + * + * INT __TMP_0 = 23; + * REAL __TMP_1 = 45.5; + * ... + */ + temp_var_name_c temp_var_name_factory; + + public: + void generate(statement_list_c *stl) { + generate_cc_tempvardecl_c generate_cc_tempvardecl(&s4o); + generate_cc_tempvardecl.generate(stl, &temp_var_name_factory); + stl->accept(*this); + } + + private: +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +void *visit(or_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " || ");} +/* TODO ... XOR expression... */ +void *visit(xor_expression_c *symbol) {ERROR; return print_binary_expression(symbol->l_exp, symbol->r_exp, " XOR ");} +void *visit(and_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " && ");} +void *visit(equ_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " == ");} +void *visit(notequ_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " != ");} +void *visit(lt_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " < ");} +void *visit(gt_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " > ");} +void *visit(le_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " <= ");} +void *visit(ge_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " >= ");} +void *visit(add_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " + ");} +void *visit(sub_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " - ");} +void *visit(mul_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " * ");} +void *visit(div_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " / ");} +void *visit(mod_expression_c *symbol) { + s4o.print("(("); + symbol->r_exp->accept(*this); + s4o.print(" == 0)?0:"); + print_binary_expression(symbol->l_exp, symbol->r_exp, " % "); + s4o.print(")"); + return NULL; +} + +/* TODO: power expression... */ +void *visit(power_expression_c *symbol) {ERROR; return print_binary_expression(symbol->l_exp, symbol->r_exp, " ** ");} +void *visit(neg_expression_c *symbol) {return print_unary_expression(symbol->exp, " -");} +void *visit(not_expression_c *symbol) {return print_unary_expression(symbol->exp, "!");} + +void *visit(function_invocation_c *symbol) { + function_declaration_c *f_decl = function_symtable.find_value(symbol->function_name); + + if (f_decl == function_symtable.end_value()) + /* should never occur. The function being called MUST be in the symtable... */ + ERROR; + + symbol->function_name->accept(*this); + s4o.print("("); + + /* loop through each function parameter, find the value we should pass + * to it, and then output the c equivalent... + */ + + function_param_iterator_c fp_iterator(f_decl); + identifier_c *param_name; + function_call_param_iterator_c function_call_param_iterator(symbol); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + if (i != 1) + s4o.print(", "); + + symbol_c *param_type = fp_iterator.param_type(); + if (param_type == NULL) ERROR; + + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* Get the value from a foo( = ) style call */ + symbol_c *param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + switch (param_direction) { + case function_param_iterator_c::direction_in: + if (param_value == NULL) { + /* No value given for parameter, so we must use the default... */ + /* First check whether default value specified in function declaration...*/ + param_value = fp_iterator.default_value(); + } + if (param_value == NULL) { + /* If not, get the default value of this variable's type */ + param_value = (symbol_c *)param_type->accept(*type_initial_value_c::instance()); + } + if (param_value == NULL) ERROR; + param_value->accept(*this); + break; + case function_param_iterator_c::direction_out: + case function_param_iterator_c::direction_inout: + if (param_value == NULL) { + /* no parameter value given, so we pass a previously declared temporary variable. */ + std::string *temp_var_name = temp_var_name_factory.new_name(); + s4o.print(*temp_var_name); + delete temp_var_name; + } else { + param_value->accept(*this); + } + break; + case function_param_iterator_c::direction_extref: + /* TODO! */ + ERROR; + break; + } /* switch */ + } /* for(...) */ + + // symbol->parameter_assignment->accept(*this); + s4o.print(")"); + return NULL; +} + +/********************/ +/* B 3.2 Statements */ +/********************/ +void *visit(statement_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +void *visit(assignment_statement_c *symbol) { + symbol->l_exp->accept(*this); + s4o.print(" = "); + symbol->r_exp->accept(*this); + return NULL; +} + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ + +/* fb_name '(' [param_assignment_list] ')' */ +/* param_assignment_list -> may be NULL ! */ +//SYM_REF2(fb_invocation_c, fb_name, param_assignment_list) +void *visit(fb_invocation_c *symbol) { + TRACE("fb_invocation_c"); + /* first figure out what is the name of the function block type of the function block being called... */ + symbol_c *function_block_type_name = this->search_fb_instance_decl->get_type_name(symbol->fb_name); + /* should never occur. The function block instance MUST have been declared... */ + if (function_block_type_name == NULL) ERROR; + + /* Now find the declaration of the function block type being called... */ + function_block_declaration_c *fb_decl = function_block_type_symtable.find_value(function_block_type_name); + /* should never occur. The function block type being called MUST be in the symtable... */ + if (fb_decl == function_block_type_symtable.end_value()) ERROR; + + /* loop through each function block parameter, find the value we should pass + * to it, and then output the c equivalent... + */ + function_param_iterator_c fp_iterator(fb_decl); + identifier_c *param_name; + function_call_param_iterator_c function_call_param_iterator(symbol); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* Get the value from a foo( = ) style call */ + symbol_c *param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + /* now output the value assignment */ + if (param_value != NULL) + if ((param_direction == function_param_iterator_c::direction_in) || + (param_direction == function_param_iterator_c::direction_inout)) { + symbol->fb_name->accept(*this); + s4o.print("."); + param_name->accept(*this); + s4o.print(" = "); + param_value->accept(*this); + s4o.print(";\n" + s4o.indent_spaces); + } + } /* for(...) */ + + /* now call the function... */ + function_block_type_name->accept(*this); + s4o.print(FB_FUNCTION_SUFFIX); + s4o.print("(&"); + symbol->fb_name->accept(*this); + s4o.print(")"); + + /* loop through each function parameter, find the variable to which + * we should atribute the value of all output or inoutput parameters. + */ + fp_iterator.reset(); + function_call_param_iterator.reset(); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* Get the value from a foo( = ) style call */ + symbol_c *param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + /* now output the value assignment */ + if (param_value != NULL) + if ((param_direction == function_param_iterator_c::direction_out) || + (param_direction == function_param_iterator_c::direction_inout)) { + s4o.print(";\n"+ s4o.indent_spaces); + param_value->accept(*this); + s4o.print(" = "); + symbol->fb_name->accept(*this); + s4o.print("."); + param_name->accept(*this); + } + } /* for(...) */ + + return NULL; +} + + + + +/* helper symbol for fb_invocation */ +/* param_assignment_list ',' param_assignment */ +void *visit(param_assignment_list_c *symbol) { + TRACE("param_assignment_list_c"); + /* this should never be called... */ + ERROR; + return NULL; +// return print_list(symbol, "", ", "); +} + + +void *visit(input_variable_param_assignment_c *symbol) { + TRACE("input_variable_param_assignment_c"); + /* this should never be called... */ + ERROR; + return NULL; +/* + symbol->variable_name->accept(*this); + s4o.print(" = "); + symbol->expression->accept(*this); + return NULL; +*/ +} + +void *visit(output_variable_param_assignment_c *symbol) { + TRACE("output_variable_param_assignment_c"); + /* this should never be called... */ + ERROR; + return NULL; +/* + s4o.print(s4o.indent_spaces); + if (symbol->not_param != NULL) + symbol->not_param->accept(*this); + symbol->variable_name->accept(*this); + s4o.print(" => "); + symbol->variable->accept(*this); + return NULL; +*/ +} + +// TODO: the NOT symbol in function (block) calls... +void *visit(not_paramassign_c *symbol) { + TRACE("not_paramassign_c"); + /* this should never be called... */ + ERROR; + return NULL; +/* + s4o.print("NOT "); + return NULL; +*/ +} + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ +void *visit(if_statement_c *symbol) { + s4o.print("if ("); + symbol->expression->accept(*this); + s4o.print(") {\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + symbol->elseif_statement_list->accept(*this); + + if (symbol->else_statement_list != NULL) { + s4o.print(s4o.indent_spaces); s4o.print("} else {\n"); + s4o.indent_right(); + symbol->else_statement_list->accept(*this); + s4o.indent_left(); + } + s4o.print(s4o.indent_spaces); s4o.print("}"); + return NULL; +} + +/* helper symbol for if_statement */ +void *visit(elseif_statement_list_c *symbol) {return print_list(symbol);} + +/* helper symbol for elseif_statement_list */ +void *visit(elseif_statement_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("} else if ("); + symbol->expression->accept(*this); + s4o.print(") {\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + return NULL; +} + +void *visit(case_statement_c *symbol) { + s4o.print("switch("); + symbol->expression->accept(*this); + s4o.print(") {\n"); + s4o.indent_right(); + symbol->case_element_list->accept(*this); + if (symbol->statement_list != NULL) { + s4o.print(s4o.indent_spaces + "default:\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.print(s4o.indent_spaces + "break;\n"); + s4o.indent_left(); + } + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "}"); + return NULL; +} + +/* helper symbol for case_statement */ +void *visit(case_element_list_c *symbol) {return print_list(symbol);} + +void *visit(case_element_c *symbol) { + s4o.print(s4o.indent_spaces + "case "); + symbol->case_list->accept(*this); + s4o.print(" :\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.print(s4o.indent_spaces + "break;\n"); + s4o.indent_left(); + return NULL; +} + +void *visit(case_list_c *symbol) {return print_list(symbol, "", ", ");} + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ +void *visit(for_statement_c *symbol) { + s4o.print("for("); + symbol->control_variable->accept(*this); + s4o.print(" = "); + symbol->beg_expression->accept(*this); + s4o.print("; "); + symbol->control_variable->accept(*this); + s4o.print(" != "); + symbol->end_expression->accept(*this); + s4o.print("; "); + symbol->control_variable->accept(*this); + if (symbol->by_expression != NULL) { + s4o.print(" += "); + symbol->by_expression->accept(*this); + } else { + s4o.print("++"); + } + s4o.print(") {\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("}"); + return NULL; +} +void *visit(while_statement_c *symbol) { + s4o.print("while ("); + symbol->expression->accept(*this); + s4o.print(") {\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("}"); + return NULL; +} +void *visit(repeat_statement_c *symbol) { + s4o.print("do {\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("} while("); + symbol->expression->accept(*this); + s4o.print(")"); + return NULL; +} +void *visit(exit_statement_c *symbol) { + s4o.print("exit(0)"); + return NULL; +} + + + +}; /* generate_cc_st_c */ + + + + + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc_tempvardecl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc_tempvardecl.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,175 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Declare temporary variables to be later used as output parameters + * in function calls for which not all output parameters were + * defined in the original (st or il) source code. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + +class temp_var_name_c { + private: + int counter; + + public: + void reset(void) {counter = 0;} + temp_var_name_c(void) {reset();} + + public: + std::string *new_name(void) { + std::string *new_str = new std::string(TEMP_VAR); + /* yikes!!! How to convert an int to a string elegantly??? + * Right now I (Mario) can only think of snprintf() + * C++ must have a more elegant method! + */ + int int_str_size = snprintf(NULL, 0, "%d", counter); + if (int_str_size <= 0) ERROR; + char *int_str = (char *)malloc(int_str_size+1); + if (snprintf(int_str, int_str_size+1, "%d", counter++) >= int_str_size+1) ERROR; + *new_str += int_str; + free(int_str); + return new_str; + } + +}; + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + +/* Some function calls in the body of functions or function blocks + * may leave some parameters to their default values, and + * ignore some output parameters of the function being called. + * Our conversion of ST functions to C++ does not contemplate that, + * i.e. each called function must get all it's input and output + * parameters set correctly. + * For input parameters we merely need to call the function with + * the apropriate default value, but for output parameters + * we must create temporary variables to hold the output value. + * + * We declare all the temporary output variables at the begining of + * the body of each function or function block, and use them as + * in function calls later on as they become necessary... + * Note that we cannot create these variables just before a function + * call, as the function call itself may be integrated within an + * expression, or another function call! + * + * The variables are declared in the exact same order in which they + * will be used later on during the function calls, which allows us + * to simply re-create the name that was used for the temporary variable + * instead of keeping it in some list. + * The names are recreated by the temp_var_name_factory, after reset() + * has been called! + * + * This function will genertae code similar to... + * + * INT __TMP_0 = 23; + * REAL __TMP_1 = 45.5; + * ... + */ + +class generate_cc_tempvardecl_c: generate_cc_typedecl_c { + public: + generate_cc_tempvardecl_c(stage4out_c *s4o_ptr): generate_cc_typedecl_c(s4o_ptr) {} + + void generate(symbol_c *body, temp_var_name_c *temp_var_name_factory) { + temp_var_name_factory->reset(); + function_call_iterator_c fcall_iterator(body); + for(symbol_c *finvocation = NULL; (finvocation = fcall_iterator.next()) != NULL;) { + /* get the name of the next function that gets called */ + identifier_c *fcalled_name = fcall_iterator.fname(); + /* get that function's declaration... */ + function_declaration_c *fdecl = function_symtable.find_value(fcalled_name); + if (fdecl == function_symtable.end_value()) ERROR; + /* create iterator to iterate through each of the called function's parameters... */ + function_param_iterator_c fp_iterator(fdecl); + + /* iterate through each of the called function's parameters... */ + identifier_c *param_name = NULL; + function_call_param_iterator_c function_call_param_iterator(finvocation); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + if (param_direction == function_param_iterator_c::direction_in) + /* ignore input only parameters... + * we do not need to create temporary variables for these! + */ + continue; + + /* Get the value from a foo( = ) style call */ + symbol_c *param_value = function_call_param_iterator.search(param_name); + + /* Get the value from a foo() style call */ + if (param_value == NULL) + param_value = function_call_param_iterator.next(); + + if (param_value != NULL) + /* ignore output parameters to which a variable is passed... + * we do not need to create temporary variables for these! + */ + continue; + + symbol_c *param_type = fp_iterator.param_type(); + + /* get the parameter's default value */ + param_value = fp_iterator.default_value(); + + /* If no default value specified in function declaration, + * get the default value of this variable's type + */ + if (param_value == NULL) + param_value = (symbol_c *)param_type->accept(*type_initial_value_c::instance()); + if (param_value == NULL) ERROR; + + /* now declare a temporary variable, with the correct default value... */ + s4o.print(s4o.indent_spaces); + param_type->accept(*this); + s4o.print(" "); + + std::string *temp_var_name = temp_var_name_factory->new_name(); + s4o.print(*temp_var_name); + delete temp_var_name; + + s4o.print(" = "); + param_value->accept(*this); + s4o.print(";\n"); + } + } + temp_var_name_factory->reset(); + s4o.print("\n"); + } +}; diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc_typedecl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc_typedecl.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,467 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Conversion of type declaration constructs. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + + + + + +class generate_cc_typedecl_c: public generate_cc_base_c { + + public: + generate_cc_typedecl_c(stage4out_c *s4o_ptr): generate_cc_base_c(s4o_ptr) {} + ~generate_cc_typedecl_c(void) {} + + +/***************************/ +/* B 0 - Programming Model */ +/***************************/ + /* leave for derived classes... */ + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ + /* done in base class(es) */ + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + /* originally empty... */ + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ + /* done in base class(es) */ + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ + /* done in base class(es) */ + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ + /* done in base class(es) */ + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ + /* done in base class(es) */ + +/**********************/ +/* B.1.3 - Data types */ +/**********************/ +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ + /* done in base class(es) */ + +/********************************/ +/* B.1.3.2 - Generic data types */ +/********************************/ + /* originally empty... */ + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +void *visit(subrange_spec_init_c *symbol) { + TRACE("subrange_spec_init_c"); + // TODO... + ERROR; + return NULL; +} + +void *visit(enumerated_spec_init_c *symbol) { + TRACE("enumerated_spec_init_c"); + // TODO... + ERROR; + return NULL; +} + +/* TYPE type_declaration_list END_TYPE */ +void *visit(data_type_declaration_c *symbol) { + TRACE("data_type_declaration_c"); + symbol->type_declaration_list->accept(*this); + s4o.print("\n\n"); + return NULL; +} + +/* helper symbol for data_type_declaration */ +void *visit(type_declaration_list_c *symbol) { + TRACE("type_declaration_list_c"); + return print_list(symbol); +} + +/* simple_type_name ':' simple_spec_init */ +void *visit(simple_type_declaration_c *symbol) { + TRACE("simple_type_declaration_c"); + /* add this type declaration to the type symbol table... */ + type_symtable.insert(symbol->simple_type_name, symbol->simple_spec_init); + + s4o.print("typedef "); + symbol->simple_spec_init->accept(*this); + s4o.print(" "); + symbol->simple_type_name->accept(*this); + s4o.print(";\n"); + return NULL; +} + +/* simple_specification [ASSIGN constant] */ +//SYM_REF2(simple_spec_init_c, simple_specification, constant) +// may be NULL +void *visit(simple_spec_init_c *symbol) { + TRACE("simple_spec_init_c"); + symbol->simple_specification->accept(*this); + return NULL; +} + +#if 0 +/* subrange_type_name ':' subrange_spec_init */ +SYM_REF2(subrange_type_declaration_c, subrange_type_name, subrange_spec_init) + +/* subrange_specification ASSIGN signed_integer */ +SYM_REF2(subrange_spec_init_c, subrange_specification, signed_integer) + +/* integer_type_name '(' subrange')' */ +SYM_REF2(subrange_specification_c, integer_type_name, subrange) + +/* signed_integer DOTDOT signed_integer */ +SYM_REF2(subrange_c, lower_limit, upper_limit) + +/* enumerated_type_name ':' enumerated_spec_init */ +SYM_REF2(enumerated_type_declaration_c, enumerated_type_name, enumerated_spec_init) + +/* enumerated_specification ASSIGN enumerated_value */ +SYM_REF2(enumerated_spec_init_c, enumerated_specification, enumerated_value) + +/* helper symbol for enumerated_specification->enumerated_spec_init */ +/* enumerated_value_list ',' enumerated_value */ +SYM_LIST(enumerated_value_list_c) + +/* enumerated_type_name '#' identifier */ +SYM_REF2(enumerated_value_c, type, value) + +/* identifier ':' array_spec_init */ +SYM_REF2(array_type_declaration_c, identifier, array_spec_init) + +/* array_specification [ASSIGN array_initialization} */ +/* array_initialization may be NULL ! */ +SYM_REF2(array_spec_init_c, array_specification, array_initialization) + +/* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ +SYM_REF2(array_specification_c, array_subrange_list, non_generic_type_name) + +/* helper symbol for array_specification */ +/* array_subrange_list ',' subrange */ +SYM_LIST(array_subrange_list_c) + +/* array_initialization: '[' array_initial_elements_list ']' */ +/* helper symbol for array_initialization */ +/* array_initial_elements_list ',' array_initial_elements */ +SYM_LIST(array_initial_elements_list_c) + +/* integer '(' [array_initial_element] ')' */ +/* array_initial_element may be NULL ! */ +SYM_REF2(array_initial_elements_c, integer, array_initial_element) +#endif + +/* structure_type_name ':' structure_specification */ +//SYM_REF2(structure_type_declaration_c, structure_type_name, structure_specification) +void *visit(structure_type_declaration_c *symbol) { + TRACE("structure_type_declaration_c"); + /* add this type declaration to the type symbol table... */ + type_symtable.insert(symbol->structure_type_name, symbol->structure_specification); + + s4o.print("typedef "); + symbol->structure_specification->accept(*this); + s4o.print(" "); + symbol->structure_type_name->accept(*this); + s4o.print(";\n"); + return NULL; +} + +/* structure_type_name ASSIGN structure_initialization */ +/* structure_initialization may be NULL ! */ +//SYM_REF2(initialized_structure_c, structure_type_name, structure_initialization) +void *visit(initialized_structure_c *symbol) { + TRACE("initialized_structure_c"); + symbol->structure_type_name->accept(*this); + return NULL; +} + +/* helper symbol for structure_declaration */ +/* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ +/* structure_element_declaration_list structure_element_declaration ';' */ +//SYM_LIST(structure_element_declaration_list_c) +void *visit(structure_element_declaration_list_c *symbol) { + TRACE("structure_element_declaration_list_c"); + s4o.print("struct {\n"); + s4o.indent_right(); + s4o.print(s4o.indent_spaces); + + print_list(symbol); + + s4o.indent_left(); + s4o.print(s4o.indent_spaces); + s4o.print("}"); + return NULL; +} + +/* structure_element_name ':' spec_init */ +//SYM_REF2(structure_element_declaration_c, structure_element_name, spec_init) +void *visit(structure_element_declaration_c *symbol) { + TRACE("structure_element_declaration_c"); + + symbol->spec_init->accept(*this); + s4o.print(" "); + symbol->structure_element_name->accept(*this); + s4o.print(";\n"); + s4o.print(s4o.indent_spaces); + + return NULL; +} + +/* helper symbol for structure_initialization */ +/* structure_initialization: '(' structure_element_initialization_list ')' */ +/* structure_element_initialization_list ',' structure_element_initialization */ +//SYM_LIST(structure_element_initialization_list_c) +void *visit(structure_element_initialization_list_c *symbol) { + TRACE("structure_element_initialization_list_c"); + + // TODO ??? + ERROR; + return NULL; +} + +/* structure_element_name ASSIGN value */ +//SYM_REF2(structure_element_initialization_c, structure_element_name, value) +void *visit(structure_element_initialization_c *symbol) { + TRACE("structure_element_initialization_c"); + + // TODO ??? + ERROR; + return NULL; +} + +#if 0 +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +/* + * NOTE: + * (Summary: Contrary to what is expected, the + * string_type_declaration_c is not used to store + * simple string type declarations that do not include + * size limits. + * For e.g.: + * str1_type: STRING := "hello!" + * will be stored in a simple_type_declaration_c + * instead of a string_type_declaration_c. + * The following: + * str2_type: STRING [64] := "hello!" + * will be stored in a sring_type_declaration_c + * + * Read on for why this is done... + * End Summary) + * + * According to the spec, the valid construct + * TYPE new_str_type : STRING := "hello!"; END_TYPE + * has two possible routes to type_declaration... + * + * Route 1: + * type_declaration: single_element_type_declaration + * single_element_type_declaration: simple_type_declaration + * simple_type_declaration: identifier ':' simple_spec_init + * simple_spec_init: simple_specification ASSIGN constant + * (shift: identifier <- 'new_str_type') + * simple_specification: elementary_type_name + * elementary_type_name: STRING + * (shift: elementary_type_name <- STRING) + * (reduce: simple_specification <- elementary_type_name) + * (shift: constant <- "hello!") + * (reduce: simple_spec_init: simple_specification ASSIGN constant) + * (reduce: ...) + * + * + * Route 2: + * type_declaration: string_type_declaration + * string_type_declaration: identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init + * (shift: identifier <- 'new_str_type') + * elementary_string_type_name: STRING + * (shift: elementary_string_type_name <- STRING) + * (shift: string_type_declaration_size <- empty ) + * string_type_declaration_init: ASSIGN character_string + * (shift: character_string <- "hello!") + * (reduce: string_type_declaration_init <- ASSIGN character_string) + * (reduce: string_type_declaration <- identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init ) + * (reduce: type_declaration <- string_type_declaration) + * + * + * At first glance it seems that removing route 1 would make + * the most sense. Unfortunately the construct 'simple_spec_init' + * shows up multiple times in other rules, so changing this construct + * would also mean changing all the rules in which it appears. + * I (Mario) therefore chose to remove route 2 instead. This means + * that the above declaration gets stored in a + * simple_type_declaration_c, and not in a string_type_declaration_c + * as would be expected! + */ +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +SYM_REF4(string_type_declaration_c, string_type_name, + elementary_string_type_name, + string_type_declaration_size, + string_type_declaration_init) /* may be == NULL! */ +#endif + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ + /* done in base class(es) */ + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +// direct_variable: direct_variable_token {$$ = new direct_variable_c($1);}; +void *visit(direct_variable_c *symbol) { + TRACE("direct_variable_c"); + /* Do not use print_token() as it will change everything into uppercase */ + if (strlen(symbol->value) == 0) ERROR; + return s4o.print(symbol->value + 1); +} + + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ + /* done in base class(es) */ + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + /* leave for derived classes... */ + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ + /* leave for derived classes... */ + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ + /* leave for derived classes... */ + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ + /* leave for derived classes... */ + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ + /* leave for derived classes... */ + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + /* leave for derived classes... */ + +/*******************/ +/* B 2.2 Operators */ +/*******************/ + /* leave for derived classes... */ + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ + /* leave for derived classes... */ + +/********************/ +/* B 3.2 Statements */ +/********************/ + /* leave for derived classes... */ + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ + /* leave for derived classes... */ + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ + /* leave for derived classes... */ + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ + /* leave for derived classes... */ + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ + /* leave for derived classes... */ + + + + +}; /* generate_cc_typedecl_c */ + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/generate_cc_vardecl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/generate_cc_vardecl.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,1661 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Conversion of variable declaration constructs. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + + + + + + + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + + +class generate_cc_vardecl_c: protected generate_cc_typedecl_c { + + /* A Helper class to the main class... */ + /* print a string, except the first time it is called */ + /* used to print the separator "," before each variable + * declaration, except the first... + * + * Needs to be done in a seperate class because this is called + * from within various locations within the code. + */ + class next_var_c { + private: + bool print_flag; + std::string str1, str2; + + next_var_c *embedded_scope; + + public: + next_var_c(std::string s1, std::string s2) { + str1 = s1; + str2 = s2; + print_flag = false; + embedded_scope = NULL; + } + + std::string get(void) { + if (NULL != embedded_scope) + return embedded_scope->get(); + + bool old_print_flag = print_flag; + print_flag = true; + if (!old_print_flag) + return str1; + else + return str2; + } + + /* Create a new next_var_c for an embedded scope. + * From now on, every call to get() will return the + * inner-most scope...! + */ + void push(std::string s1, std::string s2) { + if (NULL != embedded_scope) + return embedded_scope->push(s1, s2); + + embedded_scope = new next_var_c(s1, s2); + if (NULL == embedded_scope) + ERROR; + return; + } + + /* Remove the inner-most scope... */ + void pop(void) { + if (NULL != embedded_scope) + return embedded_scope->pop(); + + delete embedded_scope; + embedded_scope = NULL; + } + }; + + private: + /* used to generate the ',' separating the parameters in a function call */ + next_var_c *nv; + + + public: + /* the types of variables that need to be processed... */ + static const unsigned int none_vt = 0x0000; + static const unsigned int input_vt = 0x0001; // VAR_INPUT + static const unsigned int output_vt = 0x0002; // VAR_OUTPUT + static const unsigned int inoutput_vt = 0x0004; // VAR_IN_OUT + static const unsigned int private_vt = 0x0008; // VAR + static const unsigned int temp_vt = 0x0010; // VAR_TEMP + static const unsigned int external_vt = 0x0020; // VAR_EXTERNAL + static const unsigned int global_vt = 0x0040; // VAR_GLOBAL + // Globals declared inside a resource will not be declared + // unless global_vt is acompanied by resource_vt + static const unsigned int located_vt = 0x0080; // VAR AT + static const unsigned int program_vt = 0x0100; // PROGRAM (inside a configuration!) + // Programs declared inside a resource will not be declared + // unless program_vt is acompanied by resource_vt + + static const unsigned int resource_vt = 0x8000; // RESOURCE (inside a configuration!) + // This, just of itself, will not print out any declarations!! + // It must be acompanied by either program_vt and/or global_vt + + /* How variables should be declared: as local variables or + * variables within a function call interface. + * + * This will define the format of the output generated + * by this class. + * + * finterface_vf: function interface parameters + * e.g. f( int a, long b, real c ); + * --------------------- + * This class/function will produce the + * underlined code of the above examples, + * and no more!! + * + * localinit_vf: local declaration. Will include variable + * initialisation if it is not a located variable. + * e.g. + * int a = 9; + * long b = 99; + * real c = 99.9; + * + * local_vf: local declaration. Will NOT include variable + * initialisation. + * e.g. + * int a; + * long b; + * real c; + * + * init_vf: local initialisation without declaration. + * e.g. + * a = 9; + * b = 99; + * c = 99.9; + * + * constructorinit_vf: initialising of member variables... + * e.g. for a constructor... + * class_name_c(void) + * : a(9), b(99), c(99.9) { // code... } + * -------------------- + * This class/function will produce the + * underlined code of the above examples, + * and no more!! + * + * globalinit_vf: initialising of static c++ variables. These + * variables may have been declared as static inside + * a class, in which case the scope within which they were + * previously delcared must be passed as the second parameter + * to the print() function. + * + * e.g. + * __plc_pt_c START_P::loc = __plc_pt_c("I2"); + */ + typedef enum {finterface_vf, + local_vf, + localinit_vf, + init_vf, + constructorinit_vf, + globalinit_vf + } varformat_t; + + + private: + /* variable used to store the types of variables that need to be processed... */ + /* Only set in the constructor...! */ + /* Will contain a set of values of generate_cc_vardecl_c::XXXX_vt */ + unsigned int wanted_vartype; + /* variable used to store the type of variable currently being processed... */ + /* Will contain a single value of generate_cc_vardecl_c::XXXX_vt */ + unsigned int current_vartype; + + /* How variables should be declared: as local variables or + * variables within a function interface... + */ + /* Only set in the constructor...! */ + varformat_t wanted_varformat; + + /* The number of variables already declared. */ + /* Used to declare 'void' in case no variables are declared in a function interface... */ + int finterface_var_count; + + + + /* Holds the references to the type and initial value + * of the variables currently being declared. + * Please read the comment under var1_init_decl_c for further + * details... + * + * We make an effort to keep these pointers pointing to NULL + * whenever we are outside the scope of variable declaration + * (i.e. when we are traversing a part of the parse tree which + * is not part of variable declaration) in order tio try to catch + * any bugs as soon as possible. + */ + symbol_c *current_var_type_symbol; + symbol_c *current_var_init_symbol; + void update_type_init(symbol_c *symbol /* a spec_init_c, subrange_spec_init_c, etc... */ ) { + this->current_var_type_symbol = spec_init_sperator_c::get_spec(symbol); + this->current_var_init_symbol = spec_init_sperator_c::get_init(symbol); + if (NULL == this->current_var_type_symbol) + ERROR; + if (NULL == this->current_var_init_symbol) { + /* We try to find the data type's default value... */ + this->current_var_init_symbol = (symbol_c *)this->current_var_type_symbol->accept(*type_initial_value_c::instance()); + /* Note that Function Block 'data types' do not have a default value, so we cannot abort if no default value is found! */ + /* + if (NULL == this->current_var_init_symbol) + ERROR; + */ + } + } + + void void_type_init(void) { + this->current_var_type_symbol = NULL; + this->current_var_init_symbol = NULL; + } + + /* Only used when wanted_varformat == globalinit_vf + * Holds a pointer to an identifier_c, which in turns contains + * the identifier of the scope within which the static member was + * declared. + */ + symbol_c *globalnamespace; + + /* Actually produce the output where variables are declared... */ + /* Note that located variables are the exception, they + * being declared in the located_var_decl_c visitor... + */ + void *declare_variables(symbol_c *symbol) { + list_c *list = dynamic_cast(symbol); + /* should NEVER EVER occur!! */ + if (list == NULL) ERROR; + + /* now to produce the c equivalent... */ + if ((wanted_varformat == local_vf) || + (wanted_varformat == init_vf) || + (wanted_varformat == localinit_vf)) { + for(int i = 0; i < list->n; i++) { + s4o.print(s4o.indent_spaces); + if (wanted_varformat != init_vf) { + this->current_var_type_symbol->accept(*this); + s4o.print(" "); + } + this->print_variable_prefix(); + list->elements[i]->accept(*this); + if (wanted_varformat != local_vf) { + if (this->current_var_init_symbol != NULL) { + s4o.print(" = "); + this->current_var_init_symbol->accept(*this); + } + } + s4o.print(";\n"); + } + } + + if (wanted_varformat == finterface_vf) { + for(int i = 0; i < list->n; i++) { + finterface_var_count++; + s4o.print(nv->get()); + s4o.print("\n" + s4o.indent_spaces); + this->current_var_type_symbol->accept(*this); + if ((current_vartype & (output_vt | inoutput_vt)) != 0) + s4o.print(" &"); + else + s4o.print(" "); + list->elements[i]->accept(*this); + /* We do not print the initial value at function declaration! + * It is up to the caller to pass the correct default value + * if none is specified in the ST source code + */ + /* if (this->current_var_init_symbol != NULL) { + s4o.print(" = "); this->current_var_init_symbol->accept(*this);} + */ + } + } + + if (wanted_varformat == constructorinit_vf) { + for(int i = 0; i < list->n; i++) { + if (this->current_var_init_symbol != NULL) { + s4o.print(nv->get()); + list->elements[i]->accept(*this); + s4o.print("("); + this->current_var_init_symbol->accept(*this); + s4o.print(")"); + } + } + } + + return NULL; + } + + + + + public: + generate_cc_vardecl_c(stage4out_c *s4o_ptr, varformat_t varformat, unsigned int vartype) + : generate_cc_typedecl_c(s4o_ptr) { + wanted_varformat = varformat; + wanted_vartype = vartype; + current_vartype = none_vt; + current_var_type_symbol = NULL; + current_var_init_symbol = NULL; + globalnamespace = NULL; + nv = NULL; + } + + ~generate_cc_vardecl_c(void) {} + + + void print(symbol_c *symbol, symbol_c *scope = NULL, const char *variable_prefix = NULL) { + this->set_variable_prefix(variable_prefix); + if (globalinit_vf == wanted_varformat) + globalnamespace = scope; + + finterface_var_count = 0; + + switch (wanted_varformat) { + case constructorinit_vf: nv = new next_var_c(": ", ", "); break; + case finterface_vf: /* fall through... */ + case localinit_vf: /* fall through... */ + case local_vf: nv = new next_var_c("", ", "); break; + default: nv = NULL; + } /* switch() */ + + symbol->accept(*this); + + /* special case... */ + if (wanted_varformat == finterface_vf) + if (finterface_var_count == 0) + s4o.print("void"); + + delete nv; + nv = NULL; + globalnamespace = NULL; + } + + + protected: +/***************************/ +/* B 0 - Programming Model */ +/***************************/ + /* leave for derived classes... */ + +/*************************/ +/* B.1 - Common elements */ +/*************************/ +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ + /* done in base class(es) */ + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + /* originally empty... */ + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ + /* done in base class(es) */ + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ + /* done in base class(es) */ + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ + /* done in base class(es) */ + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ + /* done in base class(es) */ + +/**********************/ +/* B.1.3 - Data types */ +/**********************/ +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ + /* done in base class(es) */ + +/********************************/ +/* B.1.3.2 - Generic data types */ +/********************************/ + /* originally empty... */ + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + /* done in base class(es) */ + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ + /* done in base class(es) */ + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ + /* done in base class(es) */ + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ + /* done in base class(es) */ + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ +void *visit(constant_option_c *symbol) {s4o.print("CONSTANT"); return NULL;} +void *visit(retain_option_c *symbol) {s4o.print("RETAIN"); return NULL;} +void *visit(non_retain_option_c *symbol) {s4o.print("NON_RETAIN"); return NULL;} + +void *visit(input_declarations_c *symbol) { + TRACE("input_declarations_c"); + if ((wanted_vartype & input_vt) != 0) { +/* + // TO DO ... + if (symbol->option != NULL) + symbol->option->accept(*this); +*/ + //s4o.indent_right(); + current_vartype = input_vt; + symbol->input_declaration_list->accept(*this); + current_vartype = none_vt; + //s4o.indent_left(); + } + return NULL; +} + +/* helper symbol for input_declarations */ +void *visit(input_declaration_list_c *symbol) { + TRACE("input_declaration_list_c"); + print_list(symbol); + return NULL; +} + +void *visit(edge_declaration_c *symbol) { + TRACE("edge_declaration_c"); + // TO DO ... + symbol->var1_list->accept(*this); + s4o.print(" : BOOL "); + symbol->edge->accept(*this); + return NULL; +} + +void *visit(raising_edge_option_c *symbol) { + // TO DO ... + s4o.print("R_EDGE"); + return NULL; +} + + +#if 0 +/* var1_list ':' array_spec_init */ +SYM_REF2(array_var_init_decl_c, var1_list, array_spec_init) +#endif + +/* var1_list ':' initialized_structure */ +// SYM_REF2(structured_var_init_decl_c, var1_list, initialized_structure) +void *visit(structured_var_init_decl_c *symbol) { + TRACE("structured_var_init_decl_c"); + /* Please read the comments inside the var1_init_decl_c + * visitor, as they apply here too. + */ + + /* Start off by setting the current_var_type_symbol and + * current_var_init_symbol private variables... + */ + update_type_init(symbol->initialized_structure); + + /* now to produce the c equivalent... */ + symbol->var1_list->accept(*this); + + /* Values no longer in scope, and therefore no longer used. + * Make an effort to keep them set to NULL when not in use + * in order to catch bugs as soon as possible... + */ + void_type_init(); + + return NULL; +} + +/* fb_name_list ':' function_block_type_name ASSIGN structure_initialization */ +/* structure_initialization -> may be NULL ! */ +void *visit(fb_name_decl_c *symbol) { + TRACE("fb_name_decl_c"); + /* Please read the comments inside the var1_init_decl_c + * visitor, as they apply here too. + */ + + /* Start off by setting the current_var_type_symbol and + * current_var_init_symbol private variables... + */ + update_type_init(symbol); + + /* now to produce the c equivalent... */ + symbol->fb_name_list->accept(*this); + + /* Values no longer in scope, and therefore no longer used. + * Make an effort to keep them set to NULL when not in use + * in order to catch bugs as soon as possible... + */ + void_type_init(); + + return NULL; +} + +/* fb_name_list ',' fb_name */ +void *visit(fb_name_list_c *symbol) { + TRACE("fb_name_list_c"); + declare_variables(symbol); + return NULL; +} + + +/* VAR_OUTPUT [RETAIN | NON_RETAIN] var_init_decl_list END_VAR */ +/* option -> may be NULL ! */ +void *visit(output_declarations_c *symbol) { + TRACE("output_declarations_c"); + if ((wanted_vartype & output_vt) != 0) { +/* + // TO DO ... + if (symbol->option != NULL) + symbol->option->accept(*this); +*/ + //s4o.indent_right(); + current_vartype = output_vt; + symbol->var_init_decl_list->accept(*this); + current_vartype = none_vt; + //s4o.indent_left(); + } + return NULL; +} + +/* VAR_IN_OUT var_declaration_list END_VAR */ +void *visit(input_output_declarations_c *symbol) { + TRACE("input_output_declarations_c"); + if ((wanted_vartype & inoutput_vt) != 0) { + //s4o.indent_right(); + current_vartype = inoutput_vt; + symbol->var_declaration_list->accept(*this); + current_vartype = none_vt; + //s4o.indent_left(); + } + return NULL; +} + +/* helper symbol for input_output_declarations */ +/* var_declaration_list var_declaration ';' */ +void *visit(var_declaration_list_c *symbol) { + TRACE("var_declaration_list_c"); + print_list(symbol); + return NULL; +} + +#if 0 +/* var1_list ':' array_specification */ +SYM_REF2(array_var_declaration_c, var1_list, array_specification) +#endif + +/* var1_list ':' structure_type_name */ +//SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) +void *visit(structured_var_declaration_c *symbol) { + TRACE("structured_var_declaration_c"); + /* Please read the comments inside the var1_init_decl_c + * visitor, as they apply here too. + */ + + /* Start off by setting the current_var_type_symbol and + * current_var_init_symbol private variables... + */ + update_type_init(symbol->structure_type_name); + + /* now to produce the c equivalent... */ + symbol->var1_list->accept(*this); + + /* Values no longer in scope, and therefore no longer used. + * Make an effort to keep them set to NULL when not in use + * in order to catch bugs as soon as possible... + */ + void_type_init(); + + return NULL; +} + + +/* VAR [CONSTANT] var_init_decl_list END_VAR */ +/* option -> may be NULL ! */ +/* helper symbol for input_declarations */ +void *visit(var_declarations_c *symbol) { + TRACE("var_declarations_c"); + if ((wanted_vartype & private_vt) != 0) { +/* + // TO DO ... + if (symbol->option != NULL) + symbol->option->accept(*this); +*/ + current_vartype = private_vt; + symbol->var_init_decl_list->accept(*this); + current_vartype = none_vt; + } + return NULL; +} + +/* VAR RETAIN var_init_decl_list END_VAR */ +void *visit(retentive_var_declarations_c *symbol) { + TRACE("retentive_var_declarations_c"); + if ((wanted_vartype & private_vt) != 0) { + current_vartype = private_vt; + symbol->var_init_decl_list->accept(*this); + current_vartype = none_vt; + } + return NULL; +} + +/* VAR [CONSTANT|RETAIN|NON_RETAIN] located_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +//SYM_REF2(located_var_declarations_c, option, located_var_decl_list) +void *visit(located_var_declarations_c *symbol) { + TRACE("located_var_declarations_c"); + if ((wanted_vartype & located_vt) != 0) { +/* + // TO DO ... + if (symbol->option != NULL) + symbol->option->accept(*this); +*/ + current_vartype = located_vt; + symbol->located_var_decl_list->accept(*this); + current_vartype = none_vt; + } + return NULL; +} + +/* helper symbol for located_var_declarations */ +/* located_var_decl_list located_var_decl ';' */ +//SYM_LIST(located_var_decl_list_c) +void *visit(located_var_decl_list_c *symbol) { + TRACE("located_var_decl_list_c"); + print_list(symbol); + return NULL; +} + + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +//SYM_REF4(located_var_decl_c, variable_name, location, located_var_spec_init, unused) +void *visit(located_var_decl_c *symbol) { + TRACE("located_var_decl_c"); + /* Please read the comments inside the var1_init_decl_c + * visitor, as they apply here too. + */ + + /* Start off by setting the current_var_type_symbol and + * current_var_init_symbol private variables... + */ + update_type_init(symbol->located_var_spec_init); + + /* now to produce the c equivalent... */ + switch(wanted_varformat) { + case local_vf: + /* NOTE: located variables must be declared static, as the connection to the + * MatPLC point must be initialised at program startup, and not whenever + * a new function block is instantiated! + * Since they always refer to the same MatPLC point, it is OK to let several + * function block instances share the same located variable! + * If the IEC compiler maps IEC tasks to Linux threads, with tasks/threads + * running simultaneously, then we must make sure the MatPLC IO library + * is thread safe! + */ + s4o.print(s4o.indent_spaces + "static __plc_pt_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print(", 8*sizeof("); + this->current_var_type_symbol->accept(*this); + s4o.print(")> "); + if (symbol->variable_name != NULL) + symbol->variable_name->accept(*this); + else + symbol->location->accept(*this); + s4o.print(";\n"); + break; + + case globalinit_vf: + s4o.print(s4o.indent_spaces + "__plc_pt_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print(", 8*sizeof("); + this->current_var_type_symbol->accept(*this); + s4o.print(")> "); + if (this->globalnamespace != NULL) { + this->globalnamespace->accept(*this); + s4o.print("::"); + } + if (symbol->variable_name != NULL) + symbol->variable_name->accept(*this); + else + symbol->location->accept(*this); + + s4o.print(" = "); + + s4o.print(s4o.indent_spaces + "__plc_pt_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print(", 8*sizeof("); + this->current_var_type_symbol->accept(*this); + s4o.print(")>(\""); + symbol->location->accept(*this); + s4o.print("\""); + if (this->current_var_init_symbol != NULL) { + s4o.print(", "); + this->current_var_init_symbol->accept(*this); + } + s4o.print(");\n"); + break; + + default: + ERROR; + } /* switch() */ + + /* Values no longer in scope, and therefore no longer used. + * Make an effort to keep them set to NULL when not in use + * in order to catch bugs as soon as possible... + */ + void_type_init(); + + return NULL; +} + + + + +/*| VAR_EXTERNAL [CONSTANT] external_declaration_list END_VAR */ +/* option -> may be NULL ! */ +//SYM_REF2(external_var_declarations_c, option, external_declaration_list) +void *visit(external_var_declarations_c *symbol) { + TRACE("external_var_declarations_c"); + if ((wanted_vartype & external_vt) != 0) { +/* + // TODO ... + if (symbol->option != NULL) + symbol->option->accept(*this); +*/ + //s4o.indent_right(); + current_vartype = external_vt; + symbol->external_declaration_list->accept(*this); + current_vartype = none_vt; + //s4o.indent_left(); + } + return NULL; +} + +/* helper symbol for external_var_declarations */ +/*| external_declaration_list external_declaration';' */ +//SYM_LIST(external_declaration_list_c) +/* helper symbol for input_declarations */ +void *visit(external_declaration_list_c *symbol) { + TRACE("external_declaration_list_c"); + print_list(symbol); + return NULL; +} + + +/* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ +//SYM_REF2(external_declaration_c, global_var_name, specification) +void *visit(external_declaration_c *symbol) { + TRACE("external_declaration_c"); + /* Please read the comments inside the var1_init_decl_c + * visitor, as they apply here too. + */ + + /* Start off by setting the current_var_type_symbol and + * current_var_init_symbol private variables... + */ + this->current_var_type_symbol = symbol->specification; + this->current_var_init_symbol = NULL; + + /* now to produce the c equivalent... */ + switch (wanted_varformat) { + case local_vf: + case localinit_vf: + s4o.print(s4o.indent_spaces); + s4o.print("__ext_ref_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print("> &"); + symbol->global_var_name->accept(*this); + if ((wanted_varformat == localinit_vf) && + (this->current_var_init_symbol != NULL)) { + s4o.print(" = "); + this->current_var_init_symbol->accept(*this); + } + s4o.print(";\n"); + break; + + case constructorinit_vf: + s4o.print(nv->get()); + symbol->global_var_name->accept(*this); + s4o.print("(*"); + symbol->global_var_name->accept(*this); + s4o.print(")"); + break; + + case finterface_vf: + finterface_var_count++; + s4o.print(nv->get()); + s4o.print("__ext_ref_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print("> *"); + symbol->global_var_name->accept(*this); + break; + + default: + ERROR; + } + + /* Values no longer in scope, and therefore no longer used. + * Make an effort to keep them set to NULL when not in use + * in order to catch bugs as soon as possible... + */ + void_type_init(); + + return NULL; +} + + + +/*| VAR_GLOBAL [CONSTANT|RETAIN] global_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +// SYM_REF2(global_var_declarations_c, option, global_var_decl_list) +void *visit(global_var_declarations_c *symbol) { + TRACE("global_var_declarations_c"); + if ((wanted_vartype & global_vt) != 0) { +/* + // TODO ... + if (symbol->option != NULL) + symbol->option->accept(*this); +*/ + //s4o.indent_right(); + unsigned int previous_vartype = current_vartype; + // previous_vartype will be either none_vt, or resource_vt + current_vartype = global_vt; + symbol->global_var_decl_list->accept(*this); + current_vartype = previous_vartype; + //s4o.indent_left(); + } + return NULL; +} + +/* helper symbol for global_var_declarations */ +/*| global_var_decl_list global_var_decl ';' */ +//SYM_LIST(global_var_decl_list_c) +void *visit(global_var_decl_list_c *symbol) { + TRACE("global_var_decl_list_c"); + print_list(symbol); + return NULL; +} + + + +/*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */ +/* type_specification ->may be NULL ! */ +// SYM_REF2(global_var_decl_c, global_var_spec, type_specification) +void *visit(global_var_decl_c *symbol) { + TRACE("global_var_decl_c"); + /* Please read the comments inside the var1_init_decl_c + * visitor, as they apply here too. + */ + + /* Start off by setting the current_var_type_symbol and + * current_var_init_symbol private variables... + */ + update_type_init(symbol->type_specification); + + /* now to produce the c equivalent... */ + symbol->global_var_spec->accept(*this); + + /* Values no longer in scope, and therefore no longer used. + * Make an effort to keep them set to NULL when not in use + * in order to catch bugs as soon as possible... + */ + void_type_init(); + + return NULL; +} + + +/*| global_var_name location */ +// SYM_REF2(global_var_spec_c, global_var_name, location) +void *visit(global_var_spec_c *symbol) { + TRACE("global_var_spec_c"); + + /* now to produce the c equivalent... */ + switch(wanted_varformat) { + case local_vf: + /* NOTE: located variables must be declared static, as the connection to the + * MatPLC point must be initialised at program startup, and not whenever + * a new function block is instantiated! + * Nevertheless, this construct never occurs inside a Function Block, but + * only inside a configuration. In this case, only a single instance will + * be created, directly at startup, so it is not necessary that the variables + * be declared static. + */ + s4o.print(s4o.indent_spaces + "__plc_pt_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print(", 8*sizeof("); + this->current_var_type_symbol->accept(*this); + s4o.print(")> "); + if (symbol->global_var_name != NULL) + symbol->global_var_name->accept(*this); + else + symbol->location->accept(*this); + s4o.print(";\n"); + break; + + case constructorinit_vf: + s4o.print(nv->get()); + if (symbol->global_var_name != NULL) + symbol->global_var_name->accept(*this); + else + symbol->location->accept(*this); + s4o.print("(\""); + symbol->location->accept(*this); + s4o.print("\""); + if (this->current_var_init_symbol != NULL) { + s4o.print(", "); + this->current_var_init_symbol->accept(*this); + } + s4o.print(")"); + break; + + default: + ERROR; + } /* switch() */ + + return NULL; +} + + + +/* AT direct_variable */ +// SYM_REF2(location_c, direct_variable, unused) +void *visit(location_c *symbol) { + TRACE("location_c"); + return symbol->direct_variable->accept(*this); +} + + +/*| global_var_list ',' global_var_name */ +//SYM_LIST(global_var_list_c) +void *visit(global_var_list_c *symbol) { + TRACE("global_var_list_c"); + list_c *list = dynamic_cast(symbol); + /* should NEVER EVER occur!! */ + if (list == NULL) ERROR; + + /* now to produce the c equivalent... */ + switch (wanted_varformat) { + case local_vf: + case localinit_vf: + for(int i = 0; i < list->n; i++) { + s4o.print(s4o.indent_spaces); + s4o.print("__ext_element_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print("> "); + list->elements[i]->accept(*this); + if (wanted_varformat == localinit_vf) { + if (this->current_var_init_symbol != NULL) { + s4o.print(" = "); + this->current_var_init_symbol->accept(*this); + } + } + s4o.print(";\n"); + } + break; + + case constructorinit_vf: + if (this->current_var_init_symbol != NULL) { + for(int i = 0; i < list->n; i++) { + s4o.print(nv->get()); + + list->elements[i]->accept(*this); + s4o.print("("); + this->current_var_init_symbol->accept(*this); + s4o.print(")"); +#if 0 + /* The following code would be for globalinit_vf !! + * But it is not currently required... + */ + s4o.print(s4o.indent_spaces + "__ext_element_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print("> "); + if (this->globalnamespace != NULL) { + this->globalnamespace->accept(*this); + s4o.print("::"); + } + list->elements[i]->accept(*this); + + if (this->current_var_init_symbol != NULL) { + s4o.print(" = "); + s4o.print("__ext_element_c<"); + this->current_var_type_symbol->accept(*this); + s4o.print(">("); + this->current_var_init_symbol->accept(*this); + s4o.print(")"); + } + s4o.print(";\n"); +#endif + } + } + break; + + default: + ERROR; /* not supported, and not needed either... */ + } + + return NULL; +} + + +#if 0 +/* var1_list ':' single_byte_string_spec */ +SYM_REF2(single_byte_string_var_declaration_c, var1_list, single_byte_string_spec) + +/* STRING ['[' integer ']'] [ASSIGN single_byte_character_string] */ +/* integer ->may be NULL ! */ +/* single_byte_character_string ->may be NULL ! */ +SYM_REF2(single_byte_string_spec_c, integer, single_byte_character_string) + +/* var1_list ':' double_byte_string_spec */ +SYM_REF2(double_byte_string_var_declaration_c, var1_list, double_byte_string_spec) + +/* WSTRING ['[' integer ']'] [ASSIGN double_byte_character_string] */ +/* integer ->may be NULL ! */ +/* double_byte_character_string ->may be NULL ! */ +SYM_REF2(double_byte_string_spec_c, integer, double_byte_character_string) + +/*| VAR [RETAIN|NON_RETAIN] incompl_located_var_decl_list END_VAR */ +/* option ->may be NULL ! */ +SYM_REF2(incompl_located_var_declarations_c, option, incompl_located_var_decl_list) + +/* helper symbol for incompl_located_var_declarations */ +/*| incompl_located_var_decl_list incompl_located_var_decl ';' */ +SYM_LIST(incompl_located_var_decl_list_c) + +/* variable_name incompl_location ':' var_spec */ +SYM_REF4(incompl_located_var_decl_c, variable_name, incompl_location, var_spec, unused) + +/* AT incompl_location_token */ +SYM_TOKEN(incompl_location_c) +#endif + + +void *visit(falling_edge_option_c *symbol) { + // TO DO ... + s4o.print("F_EDGE"); + return NULL; +} + + +void *visit(var1_init_decl_c *symbol) { + TRACE("var1_init_decl_c"); + /* We have several implementation alternatives here... + * + * The issue is that generation of c code + * based on the abstract syntax tree requires the reversal + * of the symbol order compared to st. For e.g.: + * (ST) a, b, c: INT := 98; + * (C ) int a=98, b=98, c=98; + * The spec_init contains the references to 'INT' and '98'. + * The var1_list contains the references to 'a', 'b', and 'c'. + * The var1_init_decl_c contains the references to spec_init and var1_list. + * + * For c code generation, the var1_init_decl_c visitor + * would need to access the internals of other classes + * (e.g. the simple_spec_init_c). + * + * We can do this using one of three methods: + * 1) Create the abstract syntax tree differently; + * 2) Access other classes from within the var1_init_decl_c; + * 3) Pass info between the visitors using global variables + * only acessible by this class (private vars) + * + * In 1), the abstract syntax tree would be built so that + * var1_init_decl_c would contain direct references to + * var1_list_c, to the var type 'INT' and to the initialiser '98' + * (as per the example above). + * + * 2) would have several sub-options to obtain the references + * to 'INT' and '98' from within var1_init_decl_c. + * In 2a), the var1_init_decl_c would use dynamic casts to determine + * the class of spec_init (simple_spec_init_c, subrange_spec_init_c or + * enumerated_spec_init_c), and from there obtain the 'INT' and '98' + * In 2b) var1_init_decl_c would have one reference for each + * simple_spec_init_c, subrange_spec_init_c and enumerated_spec_init_c, + * the apropriate one being initialised by the var1_init_decl_c constructor. + * Note that the constructor would use dynamic casts. In essence, we + * would merely be changing the location of the code with the + * dynamic casts. + * In 2c) we would use three overloaded constructors for var1_init_decl_c + * one each for simple_spec_init_c, etc... This implies + * use type specific pointers to each symbol in the bison + * parser, instead of simply using a symbol_c * for all symbols; + * + * In 3), we use two global but private variables with references to + * 'INT' and '98', that would be initiliased by the visitors to + * simple_spec_init_c, subrange_spec_init_c and enumerated_spec_init_c. + * The visitor to var1_list_c would then use the references in the global + * variables. + * + * I (Mario) have chosen to use 3). + */ + + /* Start off by setting the current_var_type_symbol and + * current_var_init_symbol private variables... + */ + update_type_init(symbol->spec_init); + + /* now to produce the c equivalent... */ + symbol->var1_list->accept(*this); + + /* Values no longer in scope, and therefore no longer used. + * Make an effort to keep them set to NULL when not in use + * in order to catch bugs as soon as possible... + */ + void_type_init(); + + return NULL; +} + + + +void *visit(var1_list_c *symbol) { + TRACE("var1_list_c"); + declare_variables(symbol); + return NULL; +} + + + + +/* intermediate helper symbol for: + * - non_retentive_var_decls + * - output_declarations + */ +void *visit(var_init_decl_list_c *symbol) { + TRACE("var_init_decl_list_c"); + return print_list(symbol); + return NULL; +} + + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +/* The missing function_declaration_c + * is handled in derived classes + */ + + + +/* intermediate helper symbol for function_declaration */ +void *visit(var_declarations_list_c *symbol) { + TRACE("var_declarations_list_c"); + return print_list(symbol); +} + + +void *visit(function_var_decls_c *symbol) { + TRACE("function_var_decls_c"); + + if ((wanted_vartype & private_vt) != 0) { +/* + // TO DO ... + if (symbol->option != NULL) + symbol->option->accept(*this); +*/ + current_vartype = private_vt; + symbol->decl_list->accept(*this); + current_vartype = none_vt; + } + return NULL; +} + + +/* intermediate helper symbol for function_var_decls */ +void *visit(var2_init_decl_list_c *symbol) { + TRACE("var2_init_decl_list_c"); + print_list(symbol); + return NULL; +} + + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ + +/* The missing function_block_declaration_c + * is handled in derived classes + */ + + +/* VAR_TEMP temp_var_decl_list END_VAR */ +void *visit(temp_var_decls_c *symbol) { + TRACE("temp_var_decls_c"); + if ((wanted_vartype & temp_vt) != 0) { + current_vartype = temp_vt; + symbol->var_decl_list->accept(*this); + current_vartype = none_vt; + } + return NULL; +} + +/* intermediate helper symbol for temp_var_decls */ +void *visit(temp_var_decls_list_c *symbol) { + TRACE("temp_var_decls_list_c"); + return print_list(symbol); +} + +/* VAR NON_RETAIN var_init_decl_list END_VAR */ +void *visit(non_retentive_var_decls_c *symbol) { + TRACE("non_retentive_var_decls_c"); + // TODO ... guarantee the non-retain semantics! + if ((wanted_vartype & private_vt) != 0) { + current_vartype = private_vt; + symbol->var_decl_list->accept(*this); + current_vartype = none_vt; + } + return NULL; +} + + + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ + /* leave for derived classes... */ + +/*********************************************/ +/* B.1.6 Sequential function chart elements */ +/*********************************************/ + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ + /* Programs instantiated inside configurations are declared as variables!! */ + +/* +CONFIGURATION configuration_name + optional_global_var_declarations + (resource_declaration_list | single_resource_declaration) + optional_access_declarations + optional_instance_specific_initializations +END_CONFIGURATION +*/ +/* +SYM_REF6(configuration_declaration_c, configuration_name, global_var_declarations, resource_declarations, access_declarations, instance_specific_initializations, unused) +*/ +void *visit(configuration_declaration_c *symbol) { + TRACE("configuration_declaration_c"); + + symbol->global_var_declarations->accept(*this); // will contain VAR_GLOBAL declarations!! + symbol->resource_declarations->accept(*this); // will contain PROGRAM declarations!! + return NULL; +} + + +/* helper symbol for configuration_declaration */ +// SYM_LIST(resource_declaration_list_c) +void *visit(resource_declaration_list_c *symbol) { + TRACE("resource_declaration_list_c"); + + return print_list(symbol); +} + +/* +RESOURCE resource_name ON resource_type_name + optional_global_var_declarations + single_resource_declaration +END_RESOURCE +*/ +// SYM_REF4(resource_declaration_c, resource_name, resource_type_name, global_var_declarations, resource_declaration) +void *visit(resource_declaration_c *symbol) { + TRACE("resource_declaration_c"); + + if ((wanted_vartype & resource_vt) != 0) { + s4o.print(s4o.indent_spaces + "struct {\n"); + s4o.indent_right(); + + current_vartype = resource_vt; + if (NULL != symbol->global_var_declarations) + symbol->global_var_declarations->accept(*this); // will contain VAR_GLOBAL declarations!! + if (NULL != symbol->resource_declaration) + symbol->resource_declaration->accept(*this); // will contain PROGRAM declarations!! + current_vartype = none_vt; + + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "} "); + symbol->resource_name->accept(*this); + s4o.print(";\n"); + } + return NULL; +} + +/* task_configuration_list program_configuration_list */ +// SYM_REF2(single_resource_declaration_c, task_configuration_list, program_configuration_list) +void *visit(single_resource_declaration_c *symbol) { + TRACE("single_resource_declaration_c"); + + if ((wanted_vartype & program_vt) != 0) { + unsigned int previous_vartype = current_vartype; + // previous_vartype will be resource_vt + current_vartype = program_vt; + symbol->program_configuration_list->accept(*this); + current_vartype = previous_vartype; + } + return NULL; +} + + +/* helper symbol for single_resource_declaration */ +// SYM_LIST(task_configuration_list_c) + + +/* helper symbol for single_resource_declaration */ +/* | program_configuration_list program_configuration ';' */ +// SYM_LIST(program_configuration_list_c) +void *visit(program_configuration_list_c *symbol) { + TRACE("program_configuration_list_c"); + + return print_list(symbol); +} + +/* helper symbol for + * - access_path + * - instance_specific_init + */ +// SYM_LIST(any_fb_name_list_c) + +/* [resource_name '.'] global_var_name ['.' structure_element_name] */ +// SYM_REF4(global_var_reference_c, resource_name, global_var_name, structure_element_name, unused) + +/* prev_declared_program_name '.' symbolic_variable */ +// SYM_REF2(program_output_reference_c, program_name, symbolic_variable) + +/* TASK task_name task_initialization */ +// SYM_REF2(task_configuration_c, task_name, task_initialization) + +/* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ +// SYM_REF4(task_initialization_c, single_data_source, interval_data_source, priority_data_source, unused) + +/* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ +// SYM_REF6(program_configuration_c, retain_option, program_name, task_name, program_type_name, prog_conf_elements, unused) +private: + /* a helper function to the program_configuration_c visitor... */ + void program_constructor_call(program_configuration_c *symbol) { + program_declaration_c *p_decl = program_type_symtable.find_value(symbol->program_type_name); + + if (p_decl == program_type_symtable.end_value()) + /* should never occur. The program being called MUST be in the symtable... */ + ERROR; + + symbol->program_name->accept(*this); + s4o.print("("); + + /* loop through each function parameter, find the value we should pass + * to it, and then output the c equivalent... + */ + function_param_iterator_c fp_iterator(p_decl); + function_call_param_iterator_c function_call_param_iterator(symbol); + identifier_c *param_name; + nv->push("", ", "); + for(int i = 1; (param_name = fp_iterator.next()) != NULL; i++) { + + symbol_c *param_type = fp_iterator.param_type(); + if (param_type == NULL) ERROR; + + function_param_iterator_c::param_direction_t param_direction = fp_iterator.param_direction(); + + /* Get the value from a foo( = ) style call */ + symbol_c *param_value = function_call_param_iterator.search(param_name); + + switch (param_direction) { + case function_param_iterator_c::direction_in: + case function_param_iterator_c::direction_out: + case function_param_iterator_c::direction_inout: + /* ignore them all!! */ + break; + + case function_param_iterator_c::direction_extref: + if (param_value == NULL) + /* This is illegal in ST and IL languages. + * All variables declared in a VAR_EXTERNAL __must__ + * be initialised to reference a specific VAR_GLOBAL variable!! + * + * The semantic checker should have caught this, we check again just the + * same (especially since the semantic checker has not yet been written!). + */ + ERROR; + s4o.print(nv->get()); + s4o.print("&"); + param_value->accept(*this); + break; +#if 0 + if (param_value == NULL) { + /* no parameter value given, so we pass a previously declared temporary variable. */ + std::string *temp_var_name = temp_var_name_factory.new_name(); + s4o.print(*temp_var_name); + delete temp_var_name; + } else { + param_value->accept(*this); + } +#endif + break; + } /* switch */ + } /* for(...) */ + + // symbol->parameter_assignment->accept(*this); + s4o.print(")"); + nv->pop(); + return; +} + + +public: +void *visit(program_configuration_c *symbol) { + TRACE("program_configuration_c"); + + /* now to produce the c equivalent... */ + switch (wanted_varformat) { + case local_vf: + case localinit_vf: + s4o.print(s4o.indent_spaces); + symbol->program_type_name->accept(*this); + s4o.print(" "); + symbol->program_name->accept(*this); + if (wanted_varformat == localinit_vf) { + // TODO... + // program_call(symbol); + } + s4o.print(";\n"); + break; + + case constructorinit_vf: + s4o.print(nv->get()); + program_constructor_call(symbol); +/* + symbol->program_name->accept(*this); + s4o.print("("); + symbol->prog_conf_elements->accept(*this); + nv->pop(); + s4o.print(")"); +*/ + break; + + default: + ERROR; /* not supported, and not needed either... */ + } + + return NULL; +} + + + +/* prog_conf_elements ',' prog_conf_element */ +//SYM_LIST(prog_conf_elements_c) +void *visit(prog_conf_elements_c *symbol) { + TRACE("prog_conf_elements_c"); + + return print_list(symbol); +} + +/* fb_name WITH task_name */ +//SYM_REF2(fb_task_c, fb_name, task_name) +void *visit(fb_task_c *symbol) { + TRACE("fb_task_c"); + + /* TODO... + * + * NOTE: Not yet supported... + * We do not support allocating specific function blocks declared + * inside a program to be executed by a different task from the one + * already executing the program itself. + * This is mostly because I (Mario) simply do not understand the + * semantics the standard expects us to implement in this case. It is all + * very confusing, and very poorly defined in the standard! + */ + ERROR; + return NULL; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + +/* any_symbolic_variable ASSIGN prog_data_source */ +// SYM_REF2(prog_cnxn_assign_c, symbolic_variable, prog_data_source) +void *visit(prog_cnxn_assign_c *symbol) { + TRACE("prog_cnxn_assign_c"); + + /* TODO... */ + return NULL; +} + +/* any_symbolic_variable SENDTO data_sink */ +// SYM_REF2(prog_cnxn_sendto_c, symbolic_variable, prog_data_source) +void *visit(prog_cnxn_sendto_c *symbol) { + TRACE("prog_cnxn_sendto_c"); + + /* TODO... */ + return NULL; +} + +#if 0 +/* VAR_CONFIG instance_specific_init_list END_VAR_BOGUS */ +SYM_REF2(instance_specific_initializations_c, instance_specific_init_list, unused) + +/* helper symbol for instance_specific_initializations */ +SYM_LIST(instance_specific_init_list_c) + +/* resource_name '.' program_name '.' {fb_name '.'} + ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization)) +*/ +SYM_REF6(instance_specific_init_c, resource_name, program_name, any_fb_name_list, variable_name, location, initialization) + +/* helper symbol for instance_specific_init */ +/* function_block_type_name ':=' structure_initialization */ +SYM_REF2(fb_initialization_c, function_block_type_name, structure_initialization) + +#endif + + + + +/****************************************/ +/* B.2 - Language IL (Instruction List) */ +/****************************************/ +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ + /* leave for derived classes... */ + +/*******************/ +/* B 2.2 Operators */ +/*******************/ + /* leave for derived classes... */ + + +/***************************************/ +/* B.3 - Language ST (Structured Text) */ +/***************************************/ +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ + /* leave for derived classes... */ + +/********************/ +/* B 3.2 Statements */ +/********************/ + /* leave for derived classes... */ + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ + /* leave for derived classes... */ + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ + /* leave for derived classes... */ + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ + /* leave for derived classes... */ + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ + /* leave for derived classes... */ + + + +}; /* generate_cc_vardecl_c */ + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/plciec.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/plciec.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,62 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Code to be included into the code generated by the 4th stage. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + +TIME __CURRENT_TIME; + +int main(int argc, char **argv) { + struct timespec tp; + + if (plc_init(DEFAULT_MODULE_NAME, argc, argv) < 0) { + printf("Error connecting to PLC.\n"); + exit(EXIT_FAILURE); + } + __configuration_c config; + /* loop... */ + while (1) { + plc_scan_beg(); + plc_update(); + if (clock_gettime(CLOCK_REALTIME, &tp) < 0) + IEC_error(); + __CURRENT_TIME = tp; + config.run(); + plc_update(); + plc_scan_end(); + } /* while (1) */ +return -1; +} + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/plciec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/plciec.h Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,688 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Code to be included into the code generated by the 4th stage. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + +#ifndef __PLCIEC_H +#define __PLCIEC_H + +//#include +#include "plc.h" +#include +#include + + + +/* function that generates an IEC runtime error */ +void IEC_error(void) { + /* TODO... */ + fprintf(stderr, "IEC 61131-3 runtime error.\n"); + exit(1); +} + + + +typedef bool BOOL; +#define TRUE true +#define FALSE false + +typedef i8 SINT; +typedef i16 INT; +typedef i32 DINT; +typedef i64 LINT; + +typedef u8 USINT; +typedef u16 UINT; +typedef u32 UDINT; +typedef u64 ULINT; + +typedef u8 BYTE; +typedef u16 WORD; +typedef u32 DWORD; +typedef u64 LWORD; + +typedef f32 REAL; +typedef f64 LREAL; + + + + + + +/*********************************************/ +/* TIME AND DATE data trypes */ +/*********************************************/ + +/* NOTE: All the time and date data types use a struct timespec + * internally to store the time and date. This is so as to ease all the + * operations (add, subtract, multiply and division) the standard defines + * on these same data types. + * However, in order to ease the implementation of the comparison operators + * (==, =>, <=, <, >, <>), the two elements in the timespec structure + * must be handled in such a way as to guarantee the following: + * - The stored time is always the result of the operation tv_sec + tv_nsec*1e-9 + * - tv_sec and tv_nsec will always have the same sign + * (i.e. either both positive or both negative) + * - tv_nsec always holds a value in the range ]-1, +1[ seconds. + * (note that -1 and +1 are excluded form the range) + */ + + +/* NOTE: According to the C++ standard, the result of the % and / operations is implementation dependent + * when the at least one of the operands is negative! However, whatever the result of the operations, we are + * guaranteed that (a/b)*b + (a%b) is always equal to a. + * This means that, even knowing that both tv_sec and tv_sec always have the same sign (we make it so this is true), + * we must still re-normailze the result for both the addition and subtraction operations! + */ + +static inline void __normalizesign_timespec (struct timespec *ts) { + if ((ts->tv_sec > 0) && (ts->tv_nsec < 0)) { + ts->tv_sec--; + ts->tv_nsec += 1000000000; + } + if ((ts->tv_sec < 0) && (ts->tv_nsec > 0)) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000; + } +} + + +static inline struct timespec __add_timespec (const struct timespec &t1, const struct timespec &t2) { + /* NOTE the following sum works correctly because a long can hold a litle more than 2 seconds expressed in nano seconds! */ + long nsec; + nsec = t1.tv_nsec + t2.tv_nsec; + struct timespec ts; + ts.tv_sec = t1.tv_sec + t2.tv_sec + (nsec / 1000000000); + ts.tv_nsec = nsec % 1000000000; + + __normalizesign_timespec(&ts); + +return ts; +} + + +static inline struct timespec __sub_timespec (const struct timespec &t1, const struct timespec &t2) { + /* NOTE the following subtraction works correctly because a long can hold a litle more than 2 seconds expressed in nano seconds! */ + long nsec = t1.tv_nsec - t2.tv_nsec; + struct timespec ts; + ts.tv_sec = t1.tv_sec - t2.tv_sec + nsec / 1000000000; + ts.tv_nsec = nsec % 1000000000; + + __normalizesign_timespec(&ts); + + return ts; +} + + +static inline struct timespec __mul_timespec (const struct timespec &t1, const long double value) { +#if 0 + /* A simple implementation that risks reducing the precision of the TIME value */ + long double sec_d1 = t1.tv_nsec / (long double)1e9 * value; + long double sec_d2 = t1.tv_sec * value; + long int sec = (long int)truncl(sec_d1 + sec_d2); + struct timespec ts; + ts.tv_sec = sec; + ts.tv_nsec = (long int)((sec_d1 + sec_d2 - sec)*1e9); + return ts; +#else + /* A more robust implementation that reduces the loss of precision of the TIME value */ + /* NOTE: The following assumes that the value stored in tv_nsec is never larger than 1 sec + * and is also based on the fact that tv_nsec can safely store slighlty more thanb 2 sec. + */ + long double sec_d1 = t1.tv_nsec / (long double)1e9 * value; + long double sec_d2 = t1.tv_sec * value; + long int sec1 = (long int)sec_d1; + long int sec2 = (long int)sec_d2; + struct timespec ts; + ts.tv_sec = sec1 + sec2; + ts.tv_nsec = (long int)(((sec_d1 - sec1) + (sec_d2 - sec2))*1e9); + /* re-normalize the value of tv_nsec */ + /* i.e. guarantee that it falls in the range ]-1, +1[ seconds. */ + if (ts.tv_nsec >= 1000000000) {ts.tv_nsec -= 1000000000; ts.tv_sec += 1;} + if (ts.tv_nsec <= -1000000000) {ts.tv_nsec += 1000000000; ts.tv_sec -= 1;} + /* We don't need to re-normalize the sign, since we are guaranteed that tv_sec and tv_nsec + * will still both have the same sign after being multiplied by the same value. + */ + return ts; +#endif +} + +/* Helper Macro for the comparison operators... */ +#define __compare_timespec(CMP, t1, t2) ((t1.tv_sec == t2.tv_sec)? t1.tv_nsec CMP t2.tv_nsec : t1.tv_sec CMP t2.tv_sec) + + + +/* Some necessary forward declarations... */ +class TIME; +class TOD; +class DT; +class DATE; + +typedef struct timespec __timebase_t; + + + +class TIME{ + private: + /* private variable that contains the value of time. */ + /* NOTE: The stored value is _always_ (time.tv_sec + time.tv_nsec), + no matter whether tv_sec is positive or negative, or tv_nsec is positive or negative. + */ + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + TIME (void) {time.tv_sec = 0; time.tv_nsec = 0;} + TIME (__timebase_t time) {this->time = time;} + TIME (const TIME &time) {this->time = time.time;} /* copy constructor */ + TIME(int sign, double mseconds, double seconds=0, double minutes=0, double hours=0, double days=0) { + /* sign is 1 for positive values, -1 for negative time... */ + long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3; + if (sign >= 0) sign = 1; else sign = -1; + time.tv_sec = sign * (long int)total_sec; + time.tv_nsec = sign * (long int)((total_sec - time.tv_sec)*1e9); + } + + /* used in plciec.cc to set the value of the __CURRENT_TIME variable without having to call a + * TIME((__timebase_t time) constructor followed by the copy constructor. + */ + void operator= (__timebase_t time) {this->time = time;} + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator+ (const TIME &time) {return TIME(__add_timespec(this->time, time.time));} + TIME operator- (const TIME &time) {return TIME(__sub_timespec(this->time, time.time));} + + friend TOD operator+ (const TIME &time, const TOD &tod); + friend TOD operator+ (const TOD &tod, const TIME &time); + friend TOD operator- (const TOD &tod, const TIME &time); + + friend DT operator+ (const TIME &time, const DT &dt); + friend DT operator+ (const DT &dt, const TIME &time); + friend DT operator- (const DT &dt, const TIME &time); + + friend TIME operator* (const TIME &time, const long double value); + friend TIME operator* (const long double value, const TIME &time); + friend TIME operator/ (const TIME &time, const long double value); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const TIME &time) {return __compare_timespec(>, this->time, time.time);} + BOOL operator>= (const TIME &time) {return __compare_timespec(>=, this->time, time.time);} + BOOL operator< (const TIME &time) {return __compare_timespec(<, this->time, time.time);} + BOOL operator<= (const TIME &time) {return __compare_timespec(<=, this->time, time.time);} + BOOL operator== (const TIME &time) {return __compare_timespec(==, this->time, time.time);} + BOOL operator!= (const TIME &time) {return !__compare_timespec(==, this->time, time.time);} +}; + + + +/* Time of Day */ +class TOD { + private: + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + TOD (void) {time.tv_sec = 0; time.tv_nsec = 0;} + TOD (__timebase_t time) {this->time = time;} + TOD (const TOD &tod) {this->time = tod.time;} /* copy constructor */ + TOD (double seconds, double minutes=0, double hours=0) { + long double total_sec = (hours*60 + minutes)*60 + seconds; + time.tv_sec = (long int)total_sec; + time.tv_nsec = (long int)((total_sec - time.tv_sec)*1e9); + } + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator- (const TOD &tod) {return TIME(__sub_timespec(this->time, tod.time));} + + friend TOD operator+ (const TIME &time, const TOD &tod); + friend TOD operator+ (const TOD &tod, const TIME &time); + friend TOD operator- (const TOD &tod, const TIME &time); + + /* The following operation is not in the standard, + * but will ease the implementation of the default function CONCAT_DATE_TOD + */ + friend DT operator+ (const DATE &date, const TOD &tod); + friend DT operator+ (const TOD &tod, const DATE &date); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const TOD &tod) {return __compare_timespec(>, this->time, tod.time);} + BOOL operator>= (const TOD &tod) {return __compare_timespec(>=, this->time, tod.time);} + BOOL operator< (const TOD &tod) {return __compare_timespec(<, this->time, tod.time);} + BOOL operator<= (const TOD &tod) {return __compare_timespec(<=, this->time, tod.time);} + BOOL operator== (const TOD &tod) {return __compare_timespec(==, this->time, tod.time);} + BOOL operator!= (const TOD &tod) {return !__compare_timespec(==, this->time, tod.time);} +}; + + + +//typedef DATE; +class DATE { + private: + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + DATE (void) {time.tv_sec = 0; time.tv_nsec = 0;} + DATE (__timebase_t time) {this->time = time;} + DATE (const DATE &date) {this->time = date.time;} /* copy constructor */ + DATE (int day, int month, int year) { + struct tm broken_down_time; + + broken_down_time.tm_sec = 0; + broken_down_time.tm_min = 0; + broken_down_time.tm_hour = 0; + broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ + broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ + broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ + + time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ + + if ((time_t)(-1) == epoch_seconds) + IEC_error(); + + time.tv_sec = epoch_seconds; + time.tv_nsec = 0; + } + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator- (const DATE &date) {return TIME(__sub_timespec(this->time, date.time));} + /* The following operation is not in the standard, + * but will ease the implementation of the default function CONCAT_DATE_TOD + */ + friend DT operator+ (const DATE &date, const TOD &tod); + friend DT operator+ (const TOD &tod, const DATE &date); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const DATE &date) {return __compare_timespec(>, this->time, date.time);} + BOOL operator>= (const DATE &date) {return __compare_timespec(>=, this->time, date.time);} + BOOL operator< (const DATE &date) {return __compare_timespec(<, this->time, date.time);} + BOOL operator<= (const DATE &date) {return __compare_timespec(<=, this->time, date.time);} + BOOL operator== (const DATE &date) {return __compare_timespec(==, this->time, date.time);} + BOOL operator!= (const DATE &date) {return !__compare_timespec(==, this->time, date.time);} +}; + + + + + +class DT { + private: + __timebase_t time; + + public: + /* conversion to __timebase_t */ + operator __timebase_t(void) {return time;} + + /* constructors... */ + DT (void) {time.tv_sec = 0; time.tv_nsec = 0;} + DT (__timebase_t time) {this->time = time;} + DT (const DT &dt) {this->time = dt.time;} /* copy constructor */ + DT (double seconds, double minutes, double hours, int day, int month, int year) { + long double total_sec = (hours*60 + minutes)*60 + seconds; + time.tv_sec = (long int)total_sec; + time.tv_nsec = (long int)((total_sec - time.tv_sec)*1e9); + + struct tm broken_down_time; + broken_down_time.tm_sec = 0; + broken_down_time.tm_min = 0; + broken_down_time.tm_hour = 0; + broken_down_time.tm_mday = day; /* day of month, from 1 to 31 */ + broken_down_time.tm_mon = month - 1; /* month since January, in the range 0 to 11 */ + broken_down_time.tm_year = year - 1900; /* number of years since 1900 */ + + time_t epoch_seconds = mktime(&broken_down_time); /* determine number of seconds since the epoch, i.e. Jan 1st 1970 */ + if ((time_t)(-1) == epoch_seconds) + IEC_error(); + + time.tv_sec += epoch_seconds; + if (time.tv_sec < epoch_seconds) + /* since the TOD is always positive, if the above happens then we had an overflow */ + IEC_error(); + } + + /* Helpers to conversion operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + DATE __to_DATE(void) { +#if 0 + /* slow version */ + struct tm broken_down_time; + time_t seconds = time.tv_sec; + if (NULL == gmtime_r(seconds, &broken_down_time)) /* get the UTC (GMT) broken down time */ + IEC_error(); + return DATE(broken_down_time.tm_mday, broken_down_time.tm_mon, broken_down_time.tm_year); +#else + /* Faster version, based on the fact that the date will always be a multiple of 60*60*24 seconds, + * and that the value of tv_nsec falls in the range ]-1, +1[ + */ + /* The above is true since the Unix function mktime() seems to ignore all leap seconds! */ + struct timespec date_time = {time.tv_sec - (time.tv_sec % (24*60*60)), 0}; + return DATE(date_time); +#endif + } + + TOD __to_TOD(void) { +#if 0 + /* slow version */ + struct tm broken_down_time; + time_t seconds = time.tv_sec; + if (NULL == gmtime_r(seconds, &broken_down_time)) /* get the UTC (GMT) broken down time */ + IEC_error(); + return TOD(broken_down_time.tm_sec, broken_down_time.tm_min, broken_down_time.tm_hour); +#else + /* Faster version, based on the fact that the date will always be a multiple of 60*60*24 seconds + * and that the value of tv_nsec falls in the range ]-1, +1[ + */ + /* The above is true since the Unix function mktime() seems to ignore all leap seconds! */ + struct timespec time_time = {time.tv_sec % (24*60*60), time.tv_nsec}; + return TOD(time_time); +#endif + } + + /* Arithmetic operators (section 2.5.1.5.6. of version 2 of the IEC 61131-3 standard) */ + TIME operator- (const DT &dt) {return TIME(__sub_timespec(this->time, dt.time));} + + friend DT operator+ (const TIME &time, const DT &dt); + friend DT operator+ (const DT &dt, const TIME &time); + friend DT operator- (const DT &dt, const TIME &time); + + /* Comparison operators (section 2.5.1.5.4. of version 2 of the IEC 61131-3 standard) */ + BOOL operator> (const DT &dt) {return __compare_timespec(>, this->time, dt.time);} + BOOL operator>= (const DT &dt) {return __compare_timespec(>=, this->time, dt.time);} + BOOL operator< (const DT &dt) {return __compare_timespec(<, this->time, dt.time);} + BOOL operator<= (const DT &dt) {return __compare_timespec(<=, this->time, dt.time);} + BOOL operator== (const DT &dt) {return __compare_timespec(==, this->time, dt.time);} + BOOL operator!= (const DT &dt) {return !__compare_timespec(==, this->time, dt.time);} +}; + + +/* The operations on time and data types... */ +TOD operator+ (const TIME &time, const TOD &tod) {return TOD(__add_timespec(tod.time, time.time));}; +TOD operator+ (const TOD &tod, const TIME &time) {return TOD(__add_timespec(tod.time, time.time));}; +TOD operator- (const TOD &tod, const TIME &time) {return TOD(__sub_timespec(tod.time, time.time));}; + +DT operator+ (const TIME &time, const DT &dt) {return DT(__add_timespec(dt.time, time.time));}; +DT operator+ (const DT &dt, const TIME &time) {return DT(__add_timespec(dt.time, time.time));}; +DT operator- (const DT &dt, const TIME &time) {return DT(__sub_timespec(dt.time, time.time));}; + +TIME operator* (const TIME &time, const long double value) {return TIME(__mul_timespec(time.time, value));} +TIME operator* (const long double value, const TIME &time) {return TIME(__mul_timespec(time.time, value));} +TIME operator/ (const TIME &time, const long double value) {return TIME(__mul_timespec(time.time, 1.0/value));} + +/* The following operation is not in the standard, + * but will ease the implementation of the default function CONCAT_DATE_TOD + */ +DT operator+ (const DATE &date, const TOD &tod) {return DT(__add_timespec(date.time, tod.time));}; +DT operator+ (const TOD &tod, const DATE &date) {return DT(__add_timespec(date.time, tod.time));}; + + +/* global variable that will be used to implement the timers TON, TOFF and TP */ +extern TIME __CURRENT_TIME; + + + +//typedef STRING; +//typedef WSTRING; + + + +typedef union __IL_DEFVAR_T { + BOOL BOOLvar; + + SINT SINTvar; + INT INTvar; + DINT DINTvar; + LINT LINTvar; + + USINT USINTvar; + UINT UINTvar; + UDINT UDINTvar; + ULINT ULINTvar; + + BYTE BYTEvar; + WORD WORDvar; + DWORD DWORDvar; + LWORD LWORDvar; + + REAL REALvar; + LREAL LREALvar; + + /* NOTE: since the TIME, DATE, ... classes all have constructors, + * C++ does not allow them to be used as members of a union. + * The workaround is to use a base data type (in this case __timebase_t) that + * contains all the internal data these classes require, and then add an operator + * member function to each class that allows it to be converted to that same base data type, + * acompanied by a constructor using that data type. + */ + /* + TIME TIMEvar; + TOD TODvar; + DT DTvar; + DATE DATEvar; + */ + __timebase_t TIMEvar; + __timebase_t TODvar; + __timebase_t DTvar; + __timebase_t DATEvar; +} __IL_DEFVAR_T; + /*TODO TODO TODO TODO TODO TODO TODO TODO TODO + * How do we add support for the possibility of storing + * data values of derived data types into the default register, + * to be later used for calling functions, stroing in another + * variable, etc...? + * + * For example: + * TYPE + * point_t : STRUCT + * x : INT; + * y : INT; + * END_STRUCT; + * END_TYPE + * + * VAR p1, p2, p3 : point_t; + * + * LD p1 + * ST p2 + * + * + * We could do it with a pointer to void, that would contain not + * the value itself, but rather the address in which the value + * is currently stored. + * For example, we could add a + * void *generic_ptr + * to this union, and then have the above LD and ST instructions + * converted to: + * __IL_DEFVAR.generic_ptr = (void *)(&p1); + * p2 = *((point_t *)__IL_DEFVAR.generic_ptr); + * + * Unfortunately the above will only work as long as the p1 variable + * does not get a chance to change its value before the default register + * gets loaded with something esle (and therefore the value is no + * longer needed). + * Additionally, a scenario where the value of p1 may change before the + * default register gets a new value is if p1 is used in a function block + * call for an output parameter! + * For example: + * + * LD p1 + * CAL funcblock( + * param1 => p1 + * ) + * ST p2 + * + * In the above scenario, p1 gets a new value when the function block + * funcblock is called. When we get to copy the default register to + * p2, we will no longer be copying the value that got stored in the default + * register when we did 'LD p1', but rather the value returned by the + * function block call!!! + * + * How the do we implement this??? + * We will probably need to declare a default variable of the correct data + * type whenever we get these values stored to the default register. + * For example + * LD p1 + * ST p2 + * + * would be converted to: + * union { + * point_tvar point_t; + * } __IL_DEFVAR_special ; + * + * __IL_DEFVAR_special.point_tvar = p1; + * p2 = __IL_DEFVAR_special.point_tvar; + * + * The above requires that we iterate through the whole Instruction list + * before we start the conversion, in order to first determine if we need + * to declare that new variable for the default register. + * + * Since we have to do this, it would probaly be a better idea to simply + * do away with the __IL_DEFVAR_T data type we declare here, and + * declare the __IL_DEFVAR at the begining of each IL code segment + * with all the data types that get used in that segment! + */ + + +/* Names start with double underscore so as not to clash with + * names in ST or IL source code! Names including a double underscore are + * ilegal under IL and ST! + */ + +/* This is an abstract base class, that cannot be instantiated... */ +template class __ext_ref_c { + public: + virtual void operator= (value_type value) = 0; + virtual operator value_type(void) = 0; +}; + + + + + + +/* Names start with double underscore so as not to clash with + * names in ST or IL source code! Names including a double underscore are + * ilegal under IL and ST! + */ +template class __ext_element_c + : public __ext_ref_c { +//{ + + private: + value_type value; + + public: + virtual void operator= (value_type value) { + this->value = value; + } + + virtual operator value_type(void) { + return value; + } + + __ext_element_c(void) {} + + __ext_element_c(value_type value) { + this->value = value; + } +}; + + +/* Names start with double underscore so as not to clash with + * names in ST or IL source code! Names including a double underscore are + * ilegal under IL and ST! + */ +template class __plc_pt_c + : public __ext_ref_c { + + private: + plc_pt_t plc_pt; + bool valid_plc_pt; + + private: + void init_name(const char *pt_name) { + /* assume error! */ + valid_plc_pt = false; + plc_pt = plc_pt_by_name(pt_name); + if (plc_pt.valid == 0) { + plc_pt = plc_pt_null(); + return; + } + /* We can't have this check here, otherwise the boolean variables won't work correctly, + * since MatPLC uses 1 bit for boolean variables, whereas g++ uses 8 bits. + */ + /* + if (plc_pt_len(plc_pt) != size) { + plc_pt = plc_pt_null(); + return; + } + */ + valid_plc_pt = true; + } + + public: + virtual void operator= (value_type value) { + plc_set(plc_pt, *((u32 *)&value)); + } + + virtual operator value_type(void) { + u32 tmp_val = plc_get(plc_pt); + return *((value_type *)&tmp_val); + } + + __plc_pt_c(const char *pt_name) { + init_name(pt_name); + } + + __plc_pt_c(const char *pt_name, value_type init_value) { + init_name(pt_name); + *this = init_value; + } + + bool valid(void) {return valid_plc_pt;} +}; + + + +#define DEFAULT_MODULE_NAME "iec" + + + +#endif /* __PLCIEC_H */ + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/search_base_type.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/search_base_type.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,213 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* Determine the data type on which another data type is based on. + * If a new default initial value is given, we DO NOT consider it a + * new base class, and continue looking further! + * + * E.g. TYPE new_int_t : INT; END_TYPE; + * TYPE new_int2_t : INT = 2; END_TYPE; + * TYPE new_subr_t : INT (4..5); END_TYPE; + * + * new_int_t is really an INT!! + * new_int2_t is also really an INT!! + * new_subr_t is also really an INT!! + */ +class search_base_type_c: public null_visitor_c { + private: + symbol_c *current_type_name; + + public: + search_base_type_c(void) {current_type_name = NULL;} + + public: + void *visit(identifier_c *type_name) { + this->current_type_name = type_name; + /* look up the type declaration... */ + symbol_c *type_decl = type_symtable.find_value(type_name); + if (type_decl == type_symtable.end_value()) + /* Type declaration not found!! */ + ERROR; + + return type_decl->accept(*this); + } + +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ + void *visit(time_type_name_c *symbol) {return (void *)symbol;} + void *visit(bool_type_name_c *symbol) {return (void *)symbol;} + void *visit(sint_type_name_c *symbol) {return (void *)symbol;} + void *visit(int_type_name_c *symbol) {return (void *)symbol;} + void *visit(dint_type_name_c *symbol) {return (void *)symbol;} + void *visit(lint_type_name_c *symbol) {return (void *)symbol;} + void *visit(usint_type_name_c *symbol) {return (void *)symbol;} + void *visit(uint_type_name_c *symbol) {return (void *)symbol;} + void *visit(udint_type_name_c *symbol) {return (void *)symbol;} + void *visit(ulint_type_name_c *symbol) {return (void *)symbol;} + void *visit(real_type_name_c *symbol) {return (void *)symbol;} + void *visit(lreal_type_name_c *symbol) {return (void *)symbol;} + void *visit(date_type_name_c *symbol) {return (void *)symbol;} + void *visit(tod_type_name_c *symbol) {return (void *)symbol;} + void *visit(dt_type_name_c *symbol) {return (void *)symbol;} + void *visit(byte_type_name_c *symbol) {return (void *)symbol;} + void *visit(word_type_name_c *symbol) {return (void *)symbol;} + void *visit(dword_type_name_c *symbol) {return (void *)symbol;} + void *visit(lword_type_name_c *symbol) {return (void *)symbol;} + void *visit(string_type_name_c *symbol) {return (void *)symbol;} + void *visit(wstring_type_name_c *symbol) {return (void *)symbol;} + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +/* simple_type_name ':' simple_spec_init */ + void *visit(simple_type_declaration_c *symbol) { + return symbol->simple_spec_init->accept(*this); + } +/* simple_specification ASSIGN constant */ + void *visit(simple_spec_init_c *symbol) { + return symbol->simple_specification->accept(*this); + } + +/* subrange_type_name ':' subrange_spec_init */ + void *visit(subrange_type_declaration_c *symbol) { + return symbol->subrange_spec_init->accept(*this); + } + +/* subrange_specification ASSIGN signed_integer */ + void *visit(subrange_spec_init_c *symbol) { + return symbol->subrange_specification->accept(*this); + } + +/* integer_type_name '(' subrange')' */ + void *visit(subrange_specification_c *symbol) { + return symbol->integer_type_name->accept(*this); + } + +/* signed_integer DOTDOT signed_integer */ + void *visit(subrange_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* enumerated_type_name ':' enumerated_spec_init */ + void *visit(enumerated_type_declaration_c *symbol) { + this->current_type_name = symbol->enumerated_type_name; + return symbol->enumerated_spec_init->accept(*this); + } + +/* enumerated_specification ASSIGN enumerated_value */ + void *visit(enumerated_spec_init_c *symbol) { + return symbol->enumerated_specification->accept(*this); + } + +/* helper symbol for enumerated_specification->enumerated_spec_init */ +/* enumerated_value_list ',' enumerated_value */ + void *visit(enumerated_value_list_c *symbol) { + if (NULL == this->current_type_name) ERROR; + return (void *)this->current_type_name; + } + +/* enumerated_type_name '#' identifier */ +// SYM_REF2(enumerated_value_c, type, value) + void *visit(enumerated_value_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* identifier ':' array_spec_init */ + void *visit(array_type_declaration_c *symbol) { + this->current_type_name = symbol->identifier; + return symbol->array_spec_init->accept(*this); + } + +/* array_specification [ASSIGN array_initialization} */ +/* array_initialization may be NULL ! */ + void *visit(array_spec_init_c *symbol) { + return symbol->array_specification->accept(*this); + } + +/* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ + void *visit(array_specification_c *symbol) { + if (NULL == this->current_type_name) ERROR; + return (void *)this->current_type_name; + } + +/* helper symbol for array_specification */ +/* array_subrange_list ',' subrange */ + void *visit(array_subrange_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* array_initialization: '[' array_initial_elements_list ']' */ +/* helper symbol for array_initialization */ +/* array_initial_elements_list ',' array_initial_elements */ + void *visit(array_initial_elements_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* integer '(' [array_initial_element] ')' */ +/* array_initial_element may be NULL ! */ + void *visit(array_initial_elements_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* structure_type_name ':' structure_specification */ + /* NOTE: structure_specification will point to either a + * initialized_structure_c + * OR A + * structure_element_declaration_list_c + */ + void *visit(structure_type_declaration_c *symbol) { + this->current_type_name = symbol->structure_type_name; + return symbol->structure_specification->accept(*this); + } + +/* structure_type_name ASSIGN structure_initialization */ +/* structure_initialization may be NULL ! */ + void *visit(initialized_structure_c *symbol) { + return symbol->structure_type_name->accept(*this); + } + +/* helper symbol for structure_declaration */ +/* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ +/* structure_element_declaration_list structure_element_declaration ';' */ + void *visit(structure_element_declaration_list_c *symbol) { + if (NULL == this->current_type_name) ERROR; + return (void *)this->current_type_name; + } + +/* structure_element_name ':' *_spec_init */ + void *visit(structure_element_declaration_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* helper symbol for structure_initialization */ +/* structure_initialization: '(' structure_element_initialization_list ')' */ +/* structure_element_initialization_list ',' structure_element_initialization */ + void *visit(structure_element_initialization_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* structure_element_name ASSIGN value */ + void *visit(structure_element_initialization_c *symbol) {ERROR; return NULL;} /* should never get called... */ + +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +/* +SYM_REF4(string_type_declaration_c, string_type_name, + elementary_string_type_name, + string_type_declaration_size, + string_type_declaration_init) // may be == NULL! +*/ + void *visit(string_type_declaration_c *symbol) {return symbol;} +}; + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/search_constant_type.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/search_constant_type.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,130 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + + +/* Determine the data type of a specific constant or variable. + * A reference to the relevant type definition is returned. + * + * For example: + * 22 -> returns reference to a int_type_name_c object. + * 22.2 -> returns reference to a real_type_name_c object. + * LREAL#22.2 -> returns reference to a lreal_type_name_c object. + * etc... + */ +class search_constant_type_c: public search_visitor_c { + + public: +/**********************/ +/* B.1.3 - Data types */ +/**********************/ +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ + static real_type_name_c real_type_name; + static int_type_name_c int_type_name; + static string_type_name_c string_type_name; + static wstring_type_name_c wstring_type_name; + static time_type_name_c time_type_name; + static date_type_name_c date_type_name; + static dt_type_name_c dt_type_name; + static tod_type_name_c tod_type_name; + + + public: + symbol_c *get_type(symbol_c *constant) { + return (symbol_c *)constant->accept(*this); + } + + public: +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ + void *visit(real_c *symbol) {return (void *)&real_type_name;} + void *visit(integer_c *symbol) {return (void *)&int_type_name;} + void *visit(binary_integer_c *symbol) {return (void *)&int_type_name;} + void *visit(octal_integer_c *symbol) {return (void *)&int_type_name;} + void *visit(hex_integer_c *symbol) {return (void *)&int_type_name;} + + void *visit(numeric_literal_c *symbol) + {return (void *)((symbol->type!=NULL)?symbol->type:symbol->value->accept(*this));} + void *visit(integer_literal_c *symbol) + {return (void *)((symbol->type!=NULL)?symbol->type:symbol->value->accept(*this));} + void *visit(real_literal_c *symbol) + {return (void *)((symbol->type!=NULL)?symbol->type:symbol->value->accept(*this));} + void *visit(bit_string_literal_c *symbol) + {return (void *)((symbol->type!=NULL)?symbol->type:symbol->value->accept(*this));} + void *visit(boolean_literal_c *symbol) + {return (void *)((symbol->type!=NULL)?symbol->type:symbol->value->accept(*this));} + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ + void *visit(double_byte_character_string_c *symbol) {return (void *)&wstring_type_name;} + void *visit(single_byte_character_string_c *symbol) {return (void *)&string_type_name;} + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ + void *visit(neg_time_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(duration_c *symbol) {return (void *)&time_type_name;} + void *visit(fixed_point_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(days_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(hours_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(minutes_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(seconds_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(milliseconds_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ + void *visit(time_of_day_c *symbol) {return (void *)&tod_type_name;} + void *visit(daytime_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(date_c *symbol) {return (void *)&date_type_name;} + void *visit(date_literal_c *symbol) {ERROR; return NULL;} /* this member function should never be called. */ + void *visit(date_and_time_c *symbol) {return (void *)&dt_type_name;} +}; + +real_type_name_c search_constant_type_c::real_type_name; +int_type_name_c search_constant_type_c::int_type_name; +string_type_name_c search_constant_type_c::string_type_name; +wstring_type_name_c search_constant_type_c::wstring_type_name; +time_type_name_c search_constant_type_c::time_type_name; +date_type_name_c search_constant_type_c::date_type_name; +dt_type_name_c search_constant_type_c::dt_type_name; +tod_type_name_c search_constant_type_c::tod_type_name; + + + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/search_fb_instance_decl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/search_fb_instance_decl.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,141 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* Returns the function block type declaration + * of a specific function block instance. + */ + + + +/* Returns the type name of a specific function block + * instance. This class will search the variable + * declarations inside the scope given to it + * searching for the declaration of the function + * block instance. + * + * The class constructor must be given the search scope + * (function, function block or program within which + * the function block instance was declared). + * + * This class will search the tree from the root given to the + * constructor. Another option would be to build a symbol table, + * and search that instead. Building the symbol table would be done + * while visiting the variable declaration objects in the parse + * tree. Unfortuantely, generate_cc_c does not visit these + * objects, delegating it to another class. This means that + * we would need another specialised class just to build the + * symbol table. We might just as well have a specialised class + * that searches the tree itself for the relevant info. This + * class is exactly that...! + */ +class search_fb_instance_decl_c: public search_visitor_c { + + private: + symbol_c *search_scope; + + symbol_c *search_name; + symbol_c *current_fb_type_name; + + public: + search_fb_instance_decl_c(symbol_c *search_scope) { + this->search_scope = search_scope; + this->current_fb_type_name = NULL; + } + + symbol_c *get_type_name(symbol_c *fb_instance_name) { + this->search_name = fb_instance_name; + return (symbol_c *)search_scope->accept(*this); + } + + public: +/***************************/ +/* B 0 - Programming Model */ +/***************************/ + void *visit(library_c *symbol) { + /* we do not want to search multiple declaration scopes, + * so we do not visit all the functions, fucntion blocks, etc... + */ + return NULL; + } + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + +/* name_list ':' function_block_type_name ASSIGN structure_initialization */ +/* structure_initialization -> may be NULL ! */ + void *visit(fb_name_decl_c *symbol) { + current_fb_type_name = symbol->function_block_type_name; + return symbol->fb_name_list->accept(*this); + } + +/* name_list ',' fb_name */ + void *visit(fb_name_list_c *symbol) { + list_c *list = symbol; + for(int i = 0; i < list->n; i++) { + if (compare_identifiers(list->elements[i], search_name) == 0) + /* by now, current_fb_declaration should be != NULL */ + return current_fb_type_name; + } + return NULL; + } + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ + void *visit(function_declaration_c *symbol) { + /* no need to search through all the body, so we only + * visit the variable declarations...! + */ + return symbol->var_declarations_list->accept(*this); + } + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ + void *visit(function_block_declaration_c *symbol) { + /* no need to search through all the body, so we only + * visit the variable declarations...! + */ + return symbol->var_declarations->accept(*this); + } + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ + void *visit(program_declaration_c *symbol) { + /* no need to search through all the body, so we only + * visit the variable declarations...! + */ + return symbol->var_declarations->accept(*this); + } +}; + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/search_var_instance_decl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/search_var_instance_decl.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,304 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + +/* Determine the data type of a specific variable instance, including + * function block instances. + * A reference to the relevant variable declaration is returned. + * The variable instance may NOT be a member of a structure of a memeber + * of a structure of an element of an array of ... + * + * example: + * window.points[1].coordinate.x + * window.points[1].colour + * etc... ARE NOT ALLOWED! + * + * This class must only be passed the name of the variable that will appear + * in the variable declaration. In the above examples, this would be + * 'window' !! + * + * + * If you need to pass a complete name of a variable instance (such as + * 'window.points[1].coordinate.x') use the search_varfb_instance_type_c instead! + */ +/* Note that current_type_decl that this class returns may reference the + * name of a type, or the type declaration itself! + * For an example of the first, consider a variable declared as ... + * x : AAA; + * where the AAA type is previously declared as whatever. + * For an example of the second, consider a variable declared as ... + * x : array of int [10]; ----> is allowed + * + * If it is the first, we will return a reference to the name, if the second + * we return a reference to the declaration!! + */ +class search_var_instance_decl_c: public search_visitor_c { + + private: + symbol_c *search_scope; + symbol_c *search_name; + symbol_c *current_type_decl; + + public: + search_var_instance_decl_c(symbol_c *search_scope) { + this->search_scope = search_scope; + this->search_name = NULL; + this->current_type_decl = NULL; + } + + symbol_c *get_decl(symbol_c *variable_instance_name) { + this->search_name = variable_instance_name; + return (symbol_c *)search_scope->accept(*this); + } + + public: +/***************************/ +/* B 0 - Programming Model */ +/***************************/ + void *visit(library_c *symbol) { + /* we do not want to search multiple declaration scopes, + * so we do not visit all the functions, fucntion blocks, etc... + */ + return NULL; + } + + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ +/* edge -> The F_EDGE or R_EDGE directive */ +// SYM_REF2(edge_declaration_c, edge, var1_list) +// TODO + +/* var1_list is one of the following... + * simple_spec_init_c * + * subrange_spec_init_c * + * enumerated_spec_init_c * + */ +// SYM_REF2(var1_init_decl_c, var1_list, spec_init) + void *visit(var1_init_decl_c *symbol) { + current_type_decl = symbol->spec_init; + return symbol->var1_list->accept(*this); + } + +/* var1_list ',' variable_name */ +// SYM_LIST(var1_list_c) + void *visit(var1_list_c *symbol) { + list_c *list = symbol; + for(int i = 0; i < list->n; i++) { + if (compare_identifiers(list->elements[i], search_name) == 0) + /* by now, current_type_decl should be != NULL */ + return current_type_decl; + } + return NULL; + } + +/* name_list ':' function_block_type_name ASSIGN structure_initialization */ +/* structure_initialization -> may be NULL ! */ + void *visit(fb_name_decl_c *symbol) { + current_type_decl = symbol->function_block_type_name; + return symbol->fb_name_list->accept(*this); + } + +/* name_list ',' fb_name */ + void *visit(fb_name_list_c *symbol) { + list_c *list = symbol; + for(int i = 0; i < list->n; i++) { + if (compare_identifiers(list->elements[i], search_name) == 0) + /* by now, current_fb_declaration should be != NULL */ + return current_type_decl; + } + return NULL; + } + +/* var1_list ':' array_spec_init */ +// SYM_REF2(array_var_init_decl_c, var1_list, array_spec_init) + void *visit(array_var_init_decl_c *symbol) { + current_type_decl = symbol->array_spec_init; + return symbol->var1_list->accept(*this); + } + +/* var1_list ':' initialized_structure */ +// SYM_REF2(structured_var_init_decl_c, var1_list, initialized_structure) + void *visit(structured_var_init_decl_c *symbol) { + current_type_decl = symbol->initialized_structure; + return symbol->var1_list->accept(*this); + } + +/* var1_list ':' array_specification */ +// SYM_REF2(array_var_declaration_c, var1_list, array_specification) + void *visit(array_var_declaration_c *symbol) { + current_type_decl = symbol->array_specification; + return symbol->var1_list->accept(*this); + } + +/* var1_list ':' structure_type_name */ +// SYM_REF2(structured_var_declaration_c, var1_list, structure_type_name) + void *visit(structured_var_declaration_c *symbol) { + current_type_decl = symbol->structure_type_name; + return symbol->var1_list->accept(*this); + } + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +// SYM_REF4(located_var_decl_c, variable_name, location, located_var_spec_init, unused) +// TODO!! + +/* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name */ +// SYM_REF2(external_declaration_c, global_var_name, specification) + void *visit(external_declaration_c *symbol) { + if (compare_identifiers(symbol->global_var_name, search_name) == 0) + return symbol->specification; + return NULL; + } + +/*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */ +/* type_specification ->may be NULL ! */ +// SYM_REF2(global_var_decl_c, global_var_spec, type_specification) +// TODO!! + +/*| global_var_name location */ +// SYM_REF2(global_var_spec_c, global_var_name, location) +// TODO!! + +/* AT direct_variable */ +// SYM_REF2(location_c, direct_variable, unused) +// TODO!! + +/*| global_var_list ',' global_var_name */ +// SYM_LIST(global_var_list_c) +// TODO!! + +/* var1_list ':' single_byte_string_spec */ +// SYM_REF2(single_byte_string_var_declaration_c, var1_list, single_byte_string_spec) + void *visit(single_byte_string_var_declaration_c *symbol) { + current_type_decl = symbol->single_byte_string_spec; + return symbol->var1_list->accept(*this); + } + +/* STRING ['[' integer ']'] [ASSIGN single_byte_character_string] */ +/* integer ->may be NULL ! */ +/* single_byte_character_string ->may be NULL ! */ +// SYM_REF2(single_byte_string_spec_c, integer, single_byte_character_string) +// TODO!! + +/* var1_list ':' double_byte_string_spec */ +// SYM_REF2(double_byte_string_var_declaration_c, var1_list, double_byte_string_spec) + void *visit(double_byte_string_var_declaration_c *symbol) { + current_type_decl = symbol->double_byte_string_spec; + return symbol->var1_list->accept(*this); + } + +/* WSTRING ['[' integer ']'] [ASSIGN double_byte_character_string] */ +/* integer ->may be NULL ! */ +/* double_byte_character_string ->may be NULL ! */ +// SYM_REF2(double_byte_string_spec_c, integer, double_byte_character_string) +// TODO!! + +/* variable_name incompl_location ':' var_spec */ +// SYM_REF4(incompl_located_var_decl_c, variable_name, incompl_location, var_spec, unused) +// TODO!! + +/* AT incompl_location_token */ +// SYM_TOKEN(incompl_location_c) +// TODO!! + + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +// SYM_REF4(function_declaration_c, derived_function_name, type_name, var_declarations_list, function_body) + void *visit(function_declaration_c *symbol) { + /* functions have a variable named after themselves, to store + * the variable that will be returned!! + */ + if (compare_identifiers(symbol->derived_function_name, search_name) == 0) + return symbol->type_name; + + /* no need to search through all the body, so we only + * visit the variable declarations...! + */ + return symbol->var_declarations_list->accept(*this); + } + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ + void *visit(function_block_declaration_c *symbol) { + /* no need to search through all the body, so we only + * visit the variable declarations...! + */ + return symbol->var_declarations->accept(*this); + } + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ + void *visit(program_declaration_c *symbol) { + /* no need to search through all the body, so we only + * visit the variable declarations...! + */ + return symbol->var_declarations->accept(*this); + } + + +#if 0 +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +SYM_REF2(symbolic_variable_c, var_name, unused) + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +SYM_TOKEN(direct_variable_c) + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +SYM_REF2(array_variable_c, subscripted_variable, subscript_list) + +/* subscript_list ',' subscript */ +SYM_LIST(subscript_list_c) + +/* record_variable '.' field_selector */ +/* WARNING: input and/or output variables of function blocks + * may be accessed as fields of a tructured variable! + * Code handling a structured_variable_c must take + * this into account! + */ +SYM_REF2(structured_variable_c, record_variable, field_selector) + + +#endif +}; + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/search_varfb_instance_type.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/search_varfb_instance_type.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,259 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + + +/* Determine the data type of a variable. + * The variable may be a simple variable, a function block instance, a + * struture element within a data structured type (a struct or a fb), or + * an array element. + * A mixture of array element of a structure element of a structure element + * of a .... is also suported! + * + * A reference to the relevant base type __definition__ is returned. + * This means that if we find that the variable is of type MY_INT, + * which was previously declared to be + * TYPE MY_INT: INT := 9; + * this class wil return INT, and __not__ MY_INT !! + * + * + * example: + * window.points[1].coordinate.x + * window.points[1].colour + * etc... ARE ALLOWED! + * + * This class must be passed the scope within which the + * variable was declared, and the variable name... + */ +class search_varfb_instance_type_c: public search_base_type_c { + private: + search_var_instance_decl_c search_var_instance_decl; + decompose_var_instance_name_c *decompose_var_instance_name; + symbol_c *current_structelement_name; + + public: + search_varfb_instance_type_c(symbol_c *search_scope): search_var_instance_decl(search_scope) { + this->decompose_var_instance_name = NULL; + this->current_structelement_name = NULL; + } + + + symbol_c *get_type(symbol_c *variable_name) { + this->current_structelement_name = NULL; + this->decompose_var_instance_name = new decompose_var_instance_name_c(variable_name); + if (NULL == decompose_var_instance_name) ERROR; + + /* find the part of the variable name that will appear in the + * variable declaration, for e.g., in window.point.x, this would be + * window! + */ + symbol_c *var_name_part = decompose_var_instance_name->next_part(); + if (NULL == var_name_part) ERROR; + + /* Now we try to find the variable instance declaration, to determine its type... */ + symbol_c *var_decl = search_var_instance_decl.get_decl(var_name_part); + if (NULL == var_decl) { + /* variable instance declaration not found! */ + return NULL; + } + + /* if it is a struct or function block, we must search the type + * of the struct or function block member. + * This is done by this class visiting the var_decl. + * This class, while visiting, will recursively call + * decompose_var_instance_name->get_next() when and if required... + */ + symbol_c *res = (symbol_c *)var_decl->accept(*this); + if (NULL == res) ERROR; + + /* make sure that we have decomposed all strcuture elements of the variable name */ + symbol_c *var_name = decompose_var_instance_name->next_part(); + if (NULL != var_name) ERROR; + + return res; + } + + private: + /* a helper function... */ + void *visit_list(list_c *list) { + if (NULL == current_structelement_name) ERROR; + + for(int i = 0; i < list->n; i++) { + void *res = list->elements[i]->accept(*this); + if (res != NULL) + return res; + } + /* not found! */ + return NULL; + } + + /* a helper function... */ + void *base_type(symbol_c *symbol) { + search_base_type_c search_base_type; + return symbol->accept(search_base_type); + } + + + private: + /* We override the base class' visitor to identifier_c. + * This is so because the base class does not consider a function block + * to be a type, unlike this class that allows a variable instance + * of a function block type... + */ + void *visit(identifier_c *type_name) { + /* look up the type declaration... */ + symbol_c *fb_decl = function_block_type_symtable.find_value(type_name); + if (fb_decl != function_block_type_symtable.end_value()) + /* Type declaration found!! */ + return fb_decl->accept(*this); + + /* No. It is not a function block, so we let + * the base class take care of it... + */ + return search_base_type_c::visit(type_name); + } + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +/* structure_type_name ':' structure_specification */ + void *visit(structure_type_declaration_c *symbol) { + return symbol->structure_specification->accept(*this); + /* NOTE: structure_specification will point to either a + * initialized_structure_c + * OR A + * structure_element_declaration_list_c + */ + } + +/* structure_type_name ASSIGN structure_initialization */ +/* structure_initialization may be NULL ! */ +// SYM_REF2(initialized_structure_c, structure_type_name, structure_initialization) + void *visit(initialized_structure_c *symbol) { + /* make sure that we have decomposed all strcuture elements of the variable name */ + symbol_c *var_name = decompose_var_instance_name->next_part(); + if (NULL == var_name) { + /* this is it... ! + * No need to look any further... + */ + /* NOTE: we could simply do a + * return (void *)symbol; + * nevertheless, note that this search_varfb_instance_type_c + * class inherits from the search_base_type_c class, + * which means that it will usually return the base type, + * and not the derived type (*). If we are to be consistent, + * we should guarantee that we always return the base type. + * To do this we could use + * return (void *)symbol->accept(*this); + * since this class inherits from the search_base_type_c. + * However, in this case we don't want it to follow + * the structs as this search_varfb_instance_type_c does. + * We therefore have to create a new search_base_type_c + * instance to search through this type without going + * through the structs... + */ + return base_type(symbol->structure_type_name); + } + + /* now search the structure declaration */ + current_structelement_name = var_name; + /* recursively find out the data type of var_name... */ + return symbol->structure_type_name->accept(*this); + } + +/* helper symbol for structure_declaration */ +/* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ +/* structure_element_declaration_list structure_element_declaration ';' */ + void *visit(structure_element_declaration_list_c *symbol) {return visit_list(symbol);} + +/* structure_element_name ':' spec_init */ + void *visit(structure_element_declaration_c *symbol) { + if (NULL == current_structelement_name) ERROR; + + if (compare_identifiers(symbol->structure_element_name, current_structelement_name) == 0) + return symbol->spec_init->accept(*this); + + return NULL; + } + +/* helper symbol for structure_initialization */ +/* structure_initialization: '(' structure_element_initialization_list ')' */ +/* structure_element_initialization_list ',' structure_element_initialization */ + void *visit(structure_element_initialization_list_c *symbol) {ERROR; return NULL;} /* should never get called... */ +/* structure_element_name ASSIGN value */ + void *visit(structure_element_initialization_c *symbol) {ERROR; return NULL;} /* should never get called... */ + + + +/**************************************/ +/* B.1.5 - Program organization units */ +/**************************************/ +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ +/* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ +// SYM_REF4(function_block_declaration_c, fblock_name, var_declarations, fblock_body, unused) + void *visit(function_block_declaration_c *symbol) { + /* make sure that we have decomposed all strcuture elements of the variable name */ + + symbol_c *var_name = decompose_var_instance_name->next_part(); + if (NULL == var_name) { + /* this is it... ! + * No need to look any further... + * Note also that, unlike for the struct types, a function block may + * not be defined based on another (i.e. no inheritance is allowed), + * so this function block is already the most base type. + * We simply return it. + */ + return (void *)symbol; + } + + /* now search the function block declaration for the variable... */ + search_var_instance_decl_c search_decl(symbol); + symbol_c *var_decl = search_decl.get_decl(var_name); + if (NULL == var_decl) { + /* variable instance declaration not found! */ + return NULL; + } + + /* We have found the declaration. + * Should we look any further? + */ + var_name = decompose_var_instance_name->next_part(); + if (NULL == var_name) { + /* this is it... ! */ + return base_type(var_decl); + } + + current_structelement_name = var_name; + /* recursively find out the data type of var_name... */ + return symbol->var_declarations->accept(*this); + } + +}; + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/spec_init_separator.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/spec_init_separator.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,169 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Seperation of type specification and default value constructs + * (for e.g. simple_spec_init_c), into a type specificiation part, + * and a default value part. + */ + + + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + + + + + + + +class spec_init_sperator_c: public null_visitor_c { + private: + /* this is a singleton class... */ + static spec_init_sperator_c *class_instance; + static spec_init_sperator_c *get_class_instance(void) { + if (NULL == class_instance) + class_instance = new spec_init_sperator_c(); + + if (NULL == class_instance) + ERROR; + + return class_instance; + } + + private: + typedef enum {search_spec, search_init} search_what_t; + static search_what_t search_what; + + public: + /* the only two public functions... */ + static symbol_c *get_spec(symbol_c *spec_init) { + search_what = search_spec; + return (symbol_c *)spec_init->accept(*get_class_instance()); + } + + static symbol_c *get_init(symbol_c *spec_init) { + search_what = search_init; + return (symbol_c *)spec_init->accept(*get_class_instance()); + } + +// private: + public: /* probably needs to be public so it may be visited... !! */ + + +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +// SYM_TOKEN(identifier_c) +void *visit(identifier_c *symbol) { + TRACE("spec_init_sperator_c::identifier_c"); + switch (search_what) { + /* if we ever get called sith a simple identifier_c, then it must be a previously declared type... */ + case search_spec: return symbol; + case search_init: return NULL; + } + ERROR; /* should never occur */ + return NULL; +} + + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ + +/* simple_specification ASSIGN constant */ +void *visit(simple_spec_init_c *symbol) { + TRACE("spec_init_sperator_c::simple_spec_init_c"); + switch (search_what) { + case search_spec: return symbol->simple_specification; + case search_init: return symbol->constant; + } + ERROR; /* should never occur */ + return NULL; +} + +/* subrange_specification ASSIGN signed_integer */ +void *visit(subrange_spec_init_c *symbol) { + TRACE("spec_init_sperator_c::subrange_spec_init_c"); + switch (search_what) { + case search_spec: return symbol->subrange_specification; + case search_init: return symbol->signed_integer; + } + ERROR; /* should never occur */ + return NULL; +} + +/* enumerated_specification ASSIGN enumerated_value */ +void *visit(enumerated_spec_init_c *symbol) { + TRACE("spec_init_sperator_c::enumerated_spec_init_c"); + switch (search_what) { + case search_spec: return symbol->enumerated_specification; + case search_init: return symbol->enumerated_value; + } + ERROR; /* should never occur */ + return NULL; +} + +/* structure_type_name ASSIGN structure_initialization */ +/* structure_initialization may be NULL ! */ +//SYM_REF2(initialized_structure_c, structure_type_name, structure_initialization) +void *visit(initialized_structure_c *symbol) { + TRACE("spec_init_sperator_c::initialized_structure_c"); + switch (search_what) { + case search_spec: return symbol->structure_type_name; + case search_init: return symbol->structure_initialization; + } + ERROR; /* should never occur */ + return NULL; +} + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ + +/* fb_name_list ':' function_block_type_name ASSIGN structure_initialization */ +/* structure_initialization -> may be NULL ! */ +void *visit(fb_name_decl_c *symbol) { + TRACE("spec_init_sperator_c::fb_name_decl_c"); + switch (search_what) { + case search_spec: return symbol->function_block_type_name; + case search_init: return symbol->structure_initialization; + } + ERROR; /* should never occur */ + return NULL; +} + +}; /* class spec_init_sperator_c */ + + + +spec_init_sperator_c *spec_init_sperator_c ::class_instance = NULL; +spec_init_sperator_c::search_what_t spec_init_sperator_c::search_what; diff -r 000000000000 -r fb772792efd1 stage4/generate_cc/type_initial_value.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_cc/type_initial_value.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,378 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Determine the default initial value of a type declaration. + * + * This is part of the 4th stage that generates + * a c++ source program equivalent to the IL and ST + * code. + */ + + + + +//#include /* required for NULL */ +//#include +//#include + +//#include "../../util/symtable.hh" + +//#include "generate_cc.hh" + + + + + +/* Given a type definition declration, determine its default + * initial value. Note that types based on other types + * may have to iterate through each type it is based on + * to determine the initial value. + * E.g. + * TYPE + * A_t : INT := 10; + * B_t : A_t := 20; + * C_t : B_t; + * D_t : C_t := 40; + * END_TYPE + * Where the default initial value for C_t is 20! + */ +/* NOTE: The main program only needs one instance of + * this class of object. This class + * is therefore a singleton. + */ +class type_initial_value_c : public null_visitor_c { + private: + static type_initial_value_c *_instance; + /* constants for the default values of elementary data types... */ + static real_c *real_0; + static integer_c *integer_0, *integer_1; + static boolean_literal_c *bool_0; + static date_literal_c *date_literal_0; + static daytime_c *daytime_literal_0; + static duration_c *time_0; + static date_c *date_0; + static time_of_day_c *tod_0; + static date_and_time_c *dt_0; + static single_byte_character_string_c *string_0; + static double_byte_character_string_c *wstring_0; + + public: + static type_initial_value_c *instance(void) { + if (_instance != NULL) + return _instance; + + _instance = new type_initial_value_c; + + real_0 = new real_c("0"); + integer_0 = new integer_c("0"); + integer_1 = new integer_c("1"); + bool_0 = new boolean_literal_c(new bool_type_name_c(),new boolean_false_c()); + /* FIXME: Our current implementation only allows dates from 1970 onwards, + * but the standard defines the date 0001-01-01 as the default value + * for the DATE data type. Untill we fix our implementation, we use 1970-01-01 + * as our default value!! + */ +// date_literal_0 = new date_literal_c(integer_1, integer_1, integer_1); + date_literal_0 = new date_literal_c(new integer_c("1970"), integer_1, integer_1); + daytime_literal_0 = new daytime_c(integer_0, integer_0, real_0); + time_0 = new duration_c(NULL, new seconds_c(integer_0, NULL)); // T#0S + date_0 = new date_c(date_literal_0); // D#0001-01-01 + tod_0 = new time_of_day_c(daytime_literal_0); // TOD#00:00:00 + dt_0 = new date_and_time_c(date_literal_0, daytime_literal_0); // DT#0001-01-01-00:00:00 + string_0 = new single_byte_character_string_c("''"); + wstring_0 = new double_byte_character_string_c("\"\""); + + return _instance; + } + + protected: + type_initial_value_c(void) {} + + public: + symbol_c *get(identifier_c *type_name) { + TRACE("type_initial_value_c::get(): called "); + return (symbol_c *)type_name->accept(*this); + } + + + private: + void *handle_type_spec(symbol_c *base_type_name, symbol_c *type_spec_init) { + if (type_spec_init != NULL) + return type_spec_init; + /* no initial value specified, so we return + * the initial value of the type this type is based on... + */ + return base_type_name->accept(*this); + } + + public: + void *visit(identifier_c *type_name) { + /* look up the type declaration... */ + symbol_c *type_decl = type_symtable.find_value(type_name); + if (type_decl == type_symtable.end_value()) + /* Type declaration not found!! */ + /* NOTE: Variables declared out of function block 'data types', + * for eg: VAR timer: TON; END_VAR + * do not have a default value, so (TON) will never be found in the + * type symbol table. This means we cannot simply consider this + * an error and abort, but must rather return a NULL. + */ + return NULL; + + return type_decl->accept(*this); + } + +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ + void *visit(time_type_name_c *symbol) {return (void *)time_0;} + void *visit(bool_type_name_c *symbol) {return (void *)bool_0;} + void *visit(sint_type_name_c *symbol) {return (void *)integer_0;} + void *visit(int_type_name_c *symbol) {return (void *)integer_0;} + void *visit(dint_type_name_c *symbol) {return (void *)integer_0;} + void *visit(lint_type_name_c *symbol) {return (void *)integer_0;} + void *visit(usint_type_name_c *symbol) {return (void *)integer_0;} + void *visit(uint_type_name_c *symbol) {return (void *)integer_0;} + void *visit(udint_type_name_c *symbol) {return (void *)integer_0;} + void *visit(ulint_type_name_c *symbol) {return (void *)integer_0;} + void *visit(real_type_name_c *symbol) {return (void *)real_0;} + void *visit(lreal_type_name_c *symbol) {return (void *)real_0;} + void *visit(date_type_name_c *symbol) {return (void *)date_0;} + void *visit(tod_type_name_c *symbol) {return (void *)tod_0;} + void *visit(dt_type_name_c *symbol) {return (void *)dt_0;} + void *visit(byte_type_name_c *symbol) {return (void *)integer_0;} + void *visit(word_type_name_c *symbol) {return (void *)integer_0;} + void *visit(dword_type_name_c *symbol) {return (void *)integer_0;} + void *visit(lword_type_name_c *symbol) {return (void *)integer_0;} + void *visit(string_type_name_c *symbol) {return (void *)string_0;} + void *visit(wstring_type_name_c *symbol) {return (void *)wstring_0;} + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +/* simple_type_name ':' simple_spec_init */ + void *visit(simple_type_declaration_c *symbol) { + return symbol->simple_spec_init->accept(*this); + } +/* simple_specification ASSIGN constant */ + void *visit(simple_spec_init_c *symbol) { + return handle_type_spec(symbol->simple_specification, symbol->constant); + } +/* subrange_type_name ':' subrange_spec_init */ + void *visit(subrange_type_declaration_c *symbol) { + return symbol->subrange_spec_init->accept(*this); + } +/* subrange_specification ASSIGN signed_integer */ + void *visit(subrange_spec_init_c *symbol) { + return handle_type_spec(symbol->subrange_specification, symbol->signed_integer); + } +/* integer_type_name '(' subrange')' */ + void *visit(subrange_specification_c *symbol) { + /* if no initial value explicitly given, then use the lowest value of the subrange */ + return symbol->subrange->accept(*this); + } +/* signed_integer DOTDOT signed_integer */ + void *visit(subrange_c *symbol) {return symbol->lower_limit;} +/* enumerated_type_name ':' enumerated_spec_init */ + void *visit(enumerated_type_declaration_c *symbol) { + return symbol->enumerated_spec_init->accept(*this); + } +/* enumerated_specification ASSIGN enumerated_value */ + void *visit(enumerated_spec_init_c *symbol) { + return handle_type_spec(symbol->enumerated_specification, symbol->enumerated_value); + } +/* helper symbol for enumerated_specification->enumerated_spec_init */ +/* enumerated_value_list ',' enumerated_value */ + void *visit(enumerated_value_list_c *symbol) { + /* if no initial value explicitly given, then use the lowest value of the subrange */ + return (void *)symbol->elements[0]; + } +/* enumerated_type_name '#' identifier */ +// SYM_REF2(enumerated_value_c, type, value) + void *visit(enumerated_value_c *symbol) {ERROR; return NULL;} +/* identifier ':' array_spec_init */ + void *visit(array_type_declaration_c *symbol) { + return symbol->array_spec_init->accept(*this); + } +/* array_specification [ASSIGN array_initialization} */ +/* array_initialization may be NULL ! */ + void *visit(array_spec_init_c *symbol) { + return handle_type_spec(symbol->array_specification, symbol->array_initialization); + } +/* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ + void *visit(array_specification_c *symbol) { + symbol_c *init_value = (symbol_c *)symbol->non_generic_type_name->accept(*this); + + /* Now build a array_initial_elements_list_c list, and populate it + * with 1 element of the array_initial_elements_c class + */ + /* The array_initial_elements_c will contain a reference to the init_value, + * and another constant representing the number of elements in the array. + * In essence, we are building the equivilant of the following ST/IL code: + * New_array_t : ARRAY [1..30, 51..60] of INT := [40(XXX)]; + * from the user given code + * New_array_t : ARRAY [1..30, 51..60] of INT; + * and replacing XXX with the default initial value of INT. + */ + /* now we need to determine the number of elements in the array... */ + /* Easier said than done, as the array may have a list of subranges, as in the + * example given above!! + */ + /* TODO !!!!!*/ + /* For now, just assume an array with 1 element. + * I (Mario) want to finish off this part of the code before getting boged down + * in something else... + */ + // NOTE: We are leaking memory, as the integer will never get free'd!! + integer_c *integer = new integer_c("1"); + // NOTE: We are leaking memory, as the array_initial_elements will never get free'd!! + array_initial_elements_c *array_initial_elements = new array_initial_elements_c(integer, init_value); + // NOTE: We are leaking memory, as the array_initial_elements_list will never get free'd!! + array_initial_elements_list_c *array_initial_elements_list = new array_initial_elements_list_c(); + array_initial_elements_list->add_element(array_initial_elements); + return (void *)array_initial_elements_list; + } +/* helper symbol for array_specification */ +/* array_subrange_list ',' subrange */ + void *visit(array_subrange_list_c *symbol) {ERROR; return NULL;} +/* array_initialization: '[' array_initial_elements_list ']' */ +/* helper symbol for array_initialization */ +/* array_initial_elements_list ',' array_initial_elements */ + void *visit(array_initial_elements_list_c *symbol) {ERROR; return NULL;} +/* integer '(' [array_initial_element] ')' */ +/* array_initial_element may be NULL ! */ + void *visit(array_initial_elements_c *symbol) {ERROR; return NULL;} + + + + /* TODO: from this point forward... */ + +/* structure_type_name ':' structure_specification */ + void *visit(structure_type_declaration_c *symbol) {return NULL;} +/* structure_type_name ASSIGN structure_initialization */ +/* structure_initialization may be NULL ! */ + void *visit(initialized_structure_c *symbol) {return NULL;} +/* helper symbol for structure_declaration */ +/* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ +/* structure_element_declaration_list structure_element_declaration ';' */ + void *visit(structure_element_declaration_list_c *symbol) {return NULL;} +/* structure_element_name ':' *_spec_init */ + void *visit(structure_element_declaration_c *symbol) {return NULL;} +/* helper symbol for structure_initialization */ +/* structure_initialization: '(' structure_element_initialization_list ')' */ +/* structure_element_initialization_list ',' structure_element_initialization */ + void *visit(structure_element_initialization_list_c *symbol) {return NULL;} +/* structure_element_name ASSIGN value */ + void *visit(structure_element_initialization_c *symbol) {return NULL;} +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +/* + * NOTE: + * (Summary: Contrary to what is expected, the + * string_type_declaration_c is not used to store + * simple string type declarations that do not include + * size limits. + * For e.g.: + * str1_type: STRING := "hello!" + * will be stored in a simple_type_declaration_c + * instead of a string_type_declaration_c. + * The following: + * str2_type: STRING [64] := "hello!" + * will be stored in a sring_type_declaration_c + * + * Read on for why this is done... + * End Summary) + * + * According to the spec, the valid construct + * TYPE new_str_type : STRING := "hello!"; END_TYPE + * has two possible routes to type_declaration... + * + * Route 1: + * type_declaration: single_element_type_declaration + * single_element_type_declaration: simple_type_declaration + * simple_type_declaration: identifier ':' simple_spec_init + * simple_spec_init: simple_specification ASSIGN constant + * (shift: identifier <- 'new_str_type') + * simple_specification: elementary_type_name + * elementary_type_name: STRING + * (shift: elementary_type_name <- STRING) + * (reduce: simple_specification <- elementary_type_name) + * (shift: constant <- "hello!") + * (reduce: simple_spec_init: simple_specification ASSIGN constant) + * (reduce: ...) + * + * + * Route 2: + * type_declaration: string_type_declaration + * string_type_declaration: identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init + * (shift: identifier <- 'new_str_type') + * elementary_string_type_name: STRING + * (shift: elementary_string_type_name <- STRING) + * (shift: string_type_declaration_size <- empty ) + * string_type_declaration_init: ASSIGN character_string + * (shift: character_string <- "hello!") + * (reduce: string_type_declaration_init <- ASSIGN character_string) + * (reduce: string_type_declaration <- identifier ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init ) + * (reduce: type_declaration <- string_type_declaration) + * + * + * At first glance it seems that removing route 1 would make + * the most sense. Unfortunately the construct 'simple_spec_init' + * shows up multiple times in other rules, so changing this construct + * would also mean changing all the rules in which it appears. + * I (Mario) therefore chose to remove route 2 instead. This means + * that the above declaration gets stored in a + * simple_type_declaration_c, and not in a string_type_declaration_c + * as would be expected! + */ +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +#if 0 +SYM_REF4(string_type_declaration_c, string_type_name, + elementary_string_type_name, + string_type_declaration_size, + string_type_declaration_init) /* may be == NULL! */ +#endif + void *visit(string_type_declaration_c *symbol) {return NULL;} +}; + +type_initial_value_c *type_initial_value_c::_instance = NULL; +real_c *type_initial_value_c::real_0 = NULL; +integer_c *type_initial_value_c::integer_0 = NULL; +integer_c *type_initial_value_c::integer_1 = NULL; +boolean_literal_c *type_initial_value_c::bool_0 = NULL; +date_literal_c *type_initial_value_c::date_literal_0 = NULL; +daytime_c *type_initial_value_c::daytime_literal_0 = NULL; +duration_c *type_initial_value_c::time_0 = NULL; +date_c *type_initial_value_c::date_0 = NULL; +time_of_day_c *type_initial_value_c::tod_0 = NULL; +date_and_time_c *type_initial_value_c::dt_0 = NULL; +single_byte_character_string_c *type_initial_value_c::string_0 = NULL; +double_byte_character_string_c *type_initial_value_c::wstring_0 = NULL; + + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_iec/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_iec/Makefile Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,34 @@ +# include the system specific Makefile +#include ../../../../Makefile.$(shell uname) + + + +default: all + +all: generate_iec.o + +clean: + -rm -f Makefile.depend + -rm -f *.o */*.o + +#get warnings, debugging information and optimization +CXXFLAGS = -Wall -pedantic -Wpointer-arith -Wwrite-strings +# CXXFLAGS += -Werror +CXXFLAGS += -ggdb -O3 -funroll-loops +# Note: if the optimizer crashes, we'll leave out the -O3 for those files + +CXXFLAGS += -I. -I../* -I../../absyntax + + + +#how to make things from other directories if they are missing +../% /%: + $(MAKE) -C $(@D) $(@F) + + +Makefile.depend depend: + $(CXX) -MM -MG -I. *.cc \ + | perl -pe 's/:/ Makefile.depend:/' > Makefile.depend + +include Makefile.depend + diff -r 000000000000 -r fb772792efd1 stage4/generate_iec/generate_iec.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_iec/generate_iec.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,1692 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Code to be included into the code generated by the 4th stage. + * + * This is part of the 4th stage that generates + * a ST and IL source program equivalent to the IL and ST + * code. + * This is expected to be used mainly in debuging the syntax + * and lexical parser, and as a starting point for other + * 4th stages. + */ + + + + + + +// #include /* required for NULL */ +#include +#include +#include "generate_iec.hh" + +#include "../stage4.hh" + + + + + + +//class generate_iec_c: public iterator_visitor_c { +class generate_iec_c: public visitor_c { + private: + stage4out_c &s4o; + + + public: + generate_iec_c(stage4out_c *s4o_ptr): s4o(*s4o_ptr) {} + ~generate_iec_c(void) {} + + + private: + +void *print_token(token_c *token) { + return s4o.print(token->value); +} + +void *print_literal(symbol_c *type, symbol_c *value) { + type->accept(*this); + s4o.print("#"); + value->accept(*this); + return NULL; +} + +void *print_list(list_c *list, + std::string pre_elem_str = "", + std::string inter_elem_str = "", + std::string post_elem_str = "") { + if (list->n > 0) { + s4o.print(pre_elem_str); + list->elements[0]->accept(*this); + } + + for(int i = 1; i < list->n; i++) { + s4o.print(inter_elem_str); + list->elements[i]->accept(*this); + } + + if (list->n > 0) + s4o.print(post_elem_str); + + return NULL; +} + + +void *print_binary_expression(symbol_c *l_exp, + symbol_c *r_exp, + const char *operation) { + s4o.print("("); + l_exp->accept(*this); + s4o.print(operation); + r_exp->accept(*this); + s4o.print(")"); + return NULL; +} + +void *print_unary_expression(symbol_c *exp, + const char *operation) { + s4o.print(operation); + exp->accept(*this); + return NULL; +} + + + + + + public: +/***************************/ +/* 2.1.6 Pragmas */ +/***************************/ +void *visit(pragma_c *symbol) {return print_token(symbol);} + + +/***************************/ +/* B 0 - Programming Model */ +/***************************/ +void *visit(library_c *symbol) {return print_list(symbol);} + +/*******************************************/ +/* B 1.1 - Letters, digits and identifiers */ +/*******************************************/ +void *visit(identifier_c *symbol) {return print_token(symbol);} + +/*********************/ +/* B 1.2 - Constants */ +/*********************/ + +/******************************/ +/* B 1.2.1 - Numeric Literals */ +/******************************/ +void *visit(real_c *symbol) {return print_token(symbol);} +void *visit(integer_c *symbol) {return print_token(symbol);} +void *visit(binary_integer_c *symbol) {return print_token(symbol);} +void *visit(octal_integer_c *symbol) {return print_token(symbol);} +void *visit(hex_integer_c *symbol) {return print_token(symbol);} + +void *visit(numeric_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} +void *visit(integer_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} +void *visit(real_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} +void *visit(bit_string_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} +void *visit(boolean_literal_c *symbol) {return print_literal(symbol->type, symbol->value);} + +/* helper class for boolean_literal_c */ +void *visit(boolean_true_c *symbol) {s4o.print(/*"TRUE"*/"1"); return NULL;} +void *visit(boolean_false_c *symbol) {s4o.print(/*"FALSE"*/"0"); return NULL;} + +/*******************************/ +/* B.1.2.2 Character Strings */ +/*******************************/ +void *visit(double_byte_character_string_c *symbol) {return print_token(symbol);} +void *visit(single_byte_character_string_c *symbol) {return print_token(symbol);} + + +/***************************/ +/* B 1.2.3 - Time Literals */ +/***************************/ + +/************************/ +/* B 1.2.3.1 - Duration */ +/************************/ +void *visit(neg_time_c *symbol) {s4o.print("-"); return NULL;} + +void *visit(duration_c *symbol) { + s4o.print("TIME#"); + if (symbol->neg != NULL) + symbol->neg->accept(*this); + symbol->interval->accept(*this); + return NULL; +} + +void *visit(fixed_point_c *symbol) {return print_token(symbol);} + +void *visit(days_c *symbol) { + symbol->days->accept(*this); + s4o.print("d"); + if (symbol->hours != NULL) + symbol->hours->accept(*this); + return NULL; +} + +void *visit(hours_c *symbol) { + symbol->hours->accept(*this); + s4o.print("h"); + if (symbol->minutes != NULL) + symbol->minutes->accept(*this); + return NULL; +} + +void *visit(minutes_c *symbol) { + symbol->minutes->accept(*this); + s4o.print("m"); + if (symbol->seconds != NULL) + symbol->seconds->accept(*this); + return NULL; +} + +void *visit(seconds_c *symbol) { + symbol->seconds->accept(*this); + s4o.print("s"); + if (symbol->milliseconds != NULL) + symbol->milliseconds->accept(*this); + return NULL; +} + +void *visit(milliseconds_c *symbol) { + symbol->milliseconds->accept(*this); + s4o.print("ms"); + return NULL; +} + +/************************************/ +/* B 1.2.3.2 - Time of day and Date */ +/************************************/ + +void *visit(time_of_day_c *symbol) { + s4o.print("TIME_OF_DAY#"); + symbol->daytime->accept(*this); + return NULL; +} + +void *visit(daytime_c *symbol) { + symbol->day_hour->accept(*this); + s4o.print(":"); + symbol->day_minute->accept(*this); + s4o.print(":"); + symbol->day_second->accept(*this); + return NULL; +} + + +void *visit(date_c *symbol) { + s4o.print("DATE#"); + symbol->date_literal->accept(*this); + return NULL; +} + +void *visit(date_literal_c *symbol) { + symbol->year->accept(*this); + s4o.print("-"); + symbol->month->accept(*this); + s4o.print("-"); + symbol->day->accept(*this); + return NULL; +} + +void *visit(date_and_time_c *symbol) { + s4o.print("DATE_AND_TIME#"); + symbol->date_literal->accept(*this); + s4o.print("-"); + symbol->daytime->accept(*this); + return NULL; +} + + + +/***********************************/ +/* B 1.3.1 - Elementary Data Types */ +/***********************************/ +void *visit(time_type_name_c *symbol) {s4o.print("TIME"); return NULL;} +void *visit(bool_type_name_c *symbol) {s4o.print("BOOL"); return NULL;} +void *visit(sint_type_name_c *symbol) {s4o.print("SINT"); return NULL;} +void *visit(int_type_name_c *symbol) {s4o.print("INT"); return NULL;} +void *visit(dint_type_name_c *symbol) {s4o.print("DINT"); return NULL;} +void *visit(lint_type_name_c *symbol) {s4o.print("LINT"); return NULL;} +void *visit(usint_type_name_c *symbol) {s4o.print("USINT"); return NULL;} +void *visit(uint_type_name_c *symbol) {s4o.print("UINT"); return NULL;} +void *visit(udint_type_name_c *symbol) {s4o.print("UDINT"); return NULL;} +void *visit(ulint_type_name_c *symbol) {s4o.print("ULINT"); return NULL;} +void *visit(real_type_name_c *symbol) {s4o.print("REAL"); return NULL;} +void *visit(lreal_type_name_c *symbol) {s4o.print("LREAL"); return NULL;} +void *visit(date_type_name_c *symbol) {s4o.print("DATE"); return NULL;} +void *visit(tod_type_name_c *symbol) {s4o.print("TOD"); return NULL;} +void *visit(dt_type_name_c *symbol) {s4o.print("DT"); return NULL;} +void *visit(byte_type_name_c *symbol) {s4o.print("BYTE"); return NULL;} +void *visit(word_type_name_c *symbol) {s4o.print("WORD"); return NULL;} +void *visit(lword_type_name_c *symbol) {s4o.print("LWORD"); return NULL;} +void *visit(dword_type_name_c *symbol) {s4o.print("DWORD"); return NULL;} +void *visit(string_type_name_c *symbol) {s4o.print("STRING"); return NULL;} +void *visit(wstring_type_name_c *symbol) {s4o.print("WSTRING"); return NULL;} + + + +/********************************/ +/* B 1.3.3 - Derived data types */ +/********************************/ +/* TYPE type_declaration_list END_TYPE */ +void *visit(data_type_declaration_c *symbol) { + s4o.print("TYPE\n"); + s4o.indent_right(); + symbol->type_declaration_list->accept(*this); + s4o.indent_left(); + s4o.print("END_TYPE\n\n\n"); + return NULL; +} + + +/* helper symbol for data_type_declaration */ +/*| type_declaration_list type_declaration ';' */ +void *visit(type_declaration_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + + +/* simple_type_name ':' simple_spec_init */ +void *visit(simple_type_declaration_c *symbol) { + symbol->simple_type_name->accept(*this); + s4o.print(" : "); + symbol->simple_spec_init->accept(*this); + return NULL; +} + +/* simple_specification ASSIGN constant */ +void *visit(simple_spec_init_c *symbol) { + symbol->simple_specification->accept(*this); + if (symbol->constant != NULL) { + s4o.print(" := "); + symbol->constant->accept(*this); + } + return NULL; +} + +/* subrange_type_name ':' subrange_spec_init */ +void *visit(subrange_type_declaration_c *symbol) { + symbol->subrange_type_name->accept(*this); + s4o.print(" : "); + symbol->subrange_spec_init->accept(*this); + return NULL; +} + +/* subrange_specification ASSIGN signed_integer */ +void *visit(subrange_spec_init_c *symbol) { + symbol->subrange_specification->accept(*this); + if (symbol->signed_integer != NULL) { + s4o.print(" := "); + symbol->signed_integer->accept(*this); + } + return NULL; +} + +/* integer_type_name '(' subrange')' */ +void *visit(subrange_specification_c *symbol) { + symbol->integer_type_name->accept(*this); + s4o.print("("); + symbol->subrange->accept(*this); + s4o.print(")"); + return NULL; +} + +/* signed_integer DOTDOT signed_integer */ +void *visit(subrange_c *symbol) { + symbol->lower_limit->accept(*this); + s4o.print(" .. "); + symbol->upper_limit->accept(*this); + return NULL; +} + +/* enumerated_type_name ':' enumerated_spec_init */ +void *visit(enumerated_type_declaration_c *symbol) { + symbol->enumerated_type_name->accept(*this); + s4o.print(" : "); + symbol->enumerated_spec_init->accept(*this); + return NULL; +} + +/* enumerated_specification ASSIGN enumerated_value */ +void *visit(enumerated_spec_init_c *symbol) { + symbol->enumerated_specification->accept(*this); + if (symbol->enumerated_value != NULL) { + s4o.print(" := "); + symbol->enumerated_value->accept(*this); + } + return NULL; +} + +/* helper symbol for enumerated_specification->enumerated_spec_init */ +/* enumerated_value_list ',' enumerated_value */ +void *visit(enumerated_value_list_c *symbol) {print_list(symbol, "(", ", ", ")"); return NULL;} + +/* enumerated_type_name '#' identifier */ +void *visit(enumerated_value_c *symbol) { + symbol->type->accept(*this); + s4o.print("#"); + symbol->value->accept(*this); + return NULL; +} + +/* identifier ':' array_spec_init */ +void *visit(array_type_declaration_c *symbol) { + symbol->identifier->accept(*this); + s4o.print(" : "); + symbol->array_spec_init->accept(*this); + return NULL; +} + +/* array_specification [ASSIGN array_initialization} */ +/* array_initialization may be NULL ! */ +void *visit(array_spec_init_c *symbol) { + symbol->array_specification->accept(*this); + if (symbol->array_initialization != NULL) { + s4o.print(" := "); + symbol->array_initialization->accept(*this); + } + return NULL; +} + +/* ARRAY '[' array_subrange_list ']' OF non_generic_type_name */ +void *visit(array_specification_c *symbol) { + s4o.print("ARRAY ["); + symbol->array_subrange_list->accept(*this); + s4o.print("] OF "); + symbol->non_generic_type_name->accept(*this); + return NULL; +} + +/* helper symbol for array_specification */ +/* array_subrange_list ',' subrange */ +void *visit(array_subrange_list_c *symbol) {print_list(symbol, "", ", "); return NULL;} + +/* helper symbol for array_initialization */ +/* array_initial_elements_list ',' array_initial_elements */ +void *visit(array_initial_elements_list_c *symbol) {print_list(symbol, "[", ", ", "]"); return NULL;} + +/* integer '(' [array_initial_element] ')' */ +/* array_initial_element may be NULL ! */ +void *visit(array_initial_elements_c *symbol) { + symbol->integer->accept(*this); + s4o.print("("); + if (symbol->array_initial_element != NULL) + symbol->array_initial_element->accept(*this); + s4o.print(")"); + return NULL; +} + +/* structure_type_name ':' structure_specification */ +void *visit(structure_type_declaration_c *symbol) { + symbol->structure_type_name->accept(*this); + s4o.print(" : "); + symbol->structure_specification->accept(*this); + return NULL; +} + +/* structure_type_name ASSIGN structure_initialization */ +/* structure_initialization may be NULL ! */ +void *visit(initialized_structure_c *symbol) { + symbol->structure_type_name->accept(*this); + if (symbol->structure_initialization != NULL) { + s4o.print(" := "); + symbol->structure_initialization->accept(*this); + } + return NULL; +} + +/* helper symbol for structure_declaration */ +/* structure_declaration: STRUCT structure_element_declaration_list END_STRUCT */ +/* structure_element_declaration_list structure_element_declaration ';' */ +void *visit(structure_element_declaration_list_c *symbol) { + s4o.print("STRUCT\n"); + s4o.indent_right(); + print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n" + s4o.indent_spaces); + s4o.print("END_STRUCT"); + s4o.indent_left(); + return NULL; +} + +/* structure_element_name ':' *_spec_init */ +void *visit(structure_element_declaration_c *symbol) { + symbol->structure_element_name->accept(*this); + s4o.print(" : "); + symbol->spec_init->accept(*this); + return NULL; +} + +/* helper symbol for structure_initialization */ +/* structure_initialization: '(' structure_element_initialization_list ')' */ +/* structure_element_initialization_list ',' structure_element_initialization */ +void *visit(structure_element_initialization_list_c *symbol) {print_list(symbol, "(", ", ", ")"); return NULL;} + +/* structure_element_name ASSIGN value */ +void *visit(structure_element_initialization_c *symbol) { + symbol->structure_element_name->accept(*this); + s4o.print(" := "); + symbol->value->accept(*this); + return NULL; +} + +/* string_type_name ':' elementary_string_type_name string_type_declaration_size string_type_declaration_init */ +void *visit(string_type_declaration_c *symbol) { + symbol->string_type_name->accept(*this); + s4o.print(" : "); + symbol->elementary_string_type_name->accept(*this); + symbol->string_type_declaration_size->accept(*this); + if (symbol->string_type_declaration_init != NULL) + symbol->string_type_declaration_init->accept(*this); + return NULL; +} + + + + + +/*********************/ +/* B 1.4 - Variables */ +/*********************/ +void *visit(symbolic_variable_c *symbol) { + symbol->var_name->accept(*this); + return NULL; +} + +/********************************************/ +/* B.1.4.1 Directly Represented Variables */ +/********************************************/ +void *visit(direct_variable_c *symbol) {return print_token(symbol);} + + +/*************************************/ +/* B.1.4.2 Multi-element Variables */ +/*************************************/ +/* subscripted_variable '[' subscript_list ']' */ +void *visit(array_variable_c *symbol) { + symbol->subscripted_variable->accept(*this); + s4o.print("["); + symbol->subscript_list->accept(*this); + s4o.print("]"); + return NULL; +} + + +/* subscript_list ',' subscript */ +void *visit(subscript_list_c *symbol) {return print_list(symbol, "", ", ");} + +/* record_variable '.' field_selector */ +void *visit(structured_variable_c *symbol) { + symbol->record_variable->accept(*this); + s4o.print("."); + symbol->field_selector->accept(*this); + return NULL; +} + + +/******************************************/ +/* B 1.4.3 - Declaration & Initialisation */ +/******************************************/ +void *visit(constant_option_c *symbol) {s4o.print("CONSTANT"); return NULL;} +void *visit(retain_option_c *symbol) {s4o.print("RETAIN"); return NULL;} +void *visit(non_retain_option_c *symbol) {s4o.print("NON_RETAIN"); return NULL;} + +/* VAR_INPUT [RETAIN | NON_RETAIN] input_declaration_list END_VAR */ +/* option -> the RETAIN/NON_RETAIN/ directive... */ +void *visit(input_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR_INPUT "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->input_declaration_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* helper symbol for input_declarations */ +/*| input_declaration_list input_declaration ';' */ +void *visit(input_declaration_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/* edge -> The F_EDGE or R_EDGE directive */ +void *visit(edge_declaration_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : BOOL "); + symbol->edge->accept(*this); + return NULL; +} + + +void *visit(raising_edge_option_c *symbol) { + s4o.print("R_EDGE"); + return NULL; +} + +void *visit(falling_edge_option_c *symbol) { + s4o.print("F_EDGE"); + return NULL; +} + + +/* var1_list is one of the following... + * simple_spec_init_c * + * subrange_spec_init_c * + * enumerated_spec_init_c * + */ +void *visit(var1_init_decl_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : "); + symbol->spec_init->accept(*this); + return NULL; +} + + +void *visit(var1_list_c *symbol) {return print_list(symbol, "", ", ");} + + +/* var1_list ':' array_spec_init */ +void *visit(array_var_init_decl_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : "); + symbol->array_spec_init->accept(*this); + return NULL; +} + + +/* var1_list ':' initialized_structure */ +void *visit(structured_var_init_decl_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : "); + symbol->initialized_structure->accept(*this); + return NULL; +} + +/* name_list ':' function_block_type_name ASSIGN structure_initialization */ +/* structure_initialization -> may be NULL ! */ +void *visit(fb_name_decl_c *symbol) { + symbol->fb_name_list->accept(*this); + s4o.print(" : "); + symbol->function_block_type_name->accept(*this); + if (symbol->structure_initialization != NULL) { + s4o.print(" := "); + symbol->structure_initialization->accept(*this); + } + return NULL; +} + +/* name_list ',' fb_name */ +void *visit(fb_name_list_c *symbol) {return print_list(symbol, "", ", ");} + +/* VAR_OUTPUT [RETAIN | NON_RETAIN] var_init_decl_list END_VAR */ +/* option -> may be NULL ! */ +void *visit(output_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR_OUTPUT "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->var_init_decl_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* VAR_IN_OUT END_VAR */ +void *visit(input_output_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR_IN_OUT "); + s4o.print("\n"); + s4o.indent_right(); + symbol->var_declaration_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* helper symbol for input_output_declarations */ +/* var_declaration_list var_declaration ';' */ +void *visit(var_declaration_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/* var1_list ':' array_specification */ +void *visit(array_var_declaration_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : "); + symbol->array_specification->accept(*this); + return NULL; +} + +/* var1_list ':' structure_type_name */ +void *visit(structured_var_declaration_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : "); + symbol->structure_type_name->accept(*this); + return NULL; +} + +/* VAR [CONSTANT] var_init_decl_list END_VAR */ +/* option -> may be NULL ! */ +void *visit(var_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->var_init_decl_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* VAR RETAIN var_init_decl_list END_VAR */ +void *visit(retentive_var_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR RETAIN "); + s4o.print("\n"); + s4o.indent_right(); + symbol->var_init_decl_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* VAR [CONSTANT|RETAIN|NON_RETAIN] located_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +void *visit(located_var_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->located_var_decl_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* helper symbol for located_var_declarations */ +/* located_var_decl_list located_var_decl ';' */ +void *visit(located_var_decl_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/* [variable_name] location ':' located_var_spec_init */ +/* variable_name -> may be NULL ! */ +void *visit(located_var_decl_c *symbol) { + if (symbol->variable_name != NULL) { + symbol->variable_name->accept(*this); + s4o.print(" "); + } + symbol->location->accept(*this); + s4o.print(" : "); + symbol->located_var_spec_init->accept(*this); + return NULL; +} + + +/*| VAR_EXTERNAL [CONSTANT] external_declaration_list END_VAR */ +/* option -> may be NULL ! */ +void *visit(external_var_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR_EXTERNAL "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->external_declaration_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* helper symbol for external_var_declarations */ +/*| external_declaration_list external_declaration';' */ +void *visit(external_declaration_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/* global_var_name ':' (simple_specification|subrange_specification|enumerated_specification|array_specification|prev_declared_structure_type_name|function_block_type_name) */ +void *visit(external_declaration_c *symbol) { + symbol->global_var_name->accept(*this); + s4o.print(" : "); + symbol->specification->accept(*this); + return NULL; +} + +/*| VAR_GLOBAL [CONSTANT|RETAIN] global_var_decl_list END_VAR */ +/* option -> may be NULL ! */ +void *visit(global_var_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR_GLOBAL "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->global_var_decl_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* helper symbol for global_var_declarations */ +/*| global_var_decl_list global_var_decl ';' */ +void *visit(global_var_decl_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/*| global_var_spec ':' [located_var_spec_init|function_block_type_name] */ +/* type_specification ->may be NULL ! */ +void *visit(global_var_decl_c *symbol) { + symbol->global_var_spec->accept(*this); + s4o.print(" : "); + if (symbol->type_specification != NULL) + symbol->type_specification->accept(*this); + return NULL; +} + +/*| global_var_name location */ +void *visit(global_var_spec_c *symbol) { + symbol->global_var_name->accept(*this); + s4o.print(" "); + symbol->location->accept(*this); + return NULL; +} + +/* AT direct_variable */ +void *visit(location_c *symbol) { + s4o.print("AT "); + symbol->direct_variable->accept(*this); + return NULL; +} + +/*| global_var_list ',' global_var_name */ +void *visit(global_var_list_c *symbol) {return print_list(symbol, "", ", ");} + +/* var1_list ':' single_byte_string_spec */ +void *visit(single_byte_string_var_declaration_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : "); + symbol->single_byte_string_spec->accept(*this); + return NULL; +} + +/* STRING ['[' integer ']'] [ASSIGN single_byte_character_string] */ +/* integer ->may be NULL ! */ +/* single_byte_character_string ->may be NULL ! */ +void *visit(single_byte_string_spec_c *symbol) { + s4o.print("STRING ["); + if (symbol->integer != NULL) + symbol->integer->accept(*this); + s4o.print("]"); + if (symbol->single_byte_character_string != NULL) { + s4o.print(" := "); + symbol->single_byte_character_string->accept(*this); + } + return NULL; +} + +/* var1_list ':' double_byte_string_spec */ +void *visit(double_byte_string_var_declaration_c *symbol) { + symbol->var1_list->accept(*this); + s4o.print(" : "); + symbol->double_byte_string_spec->accept(*this); + return NULL; +} + +/* WSTRING ['[' integer ']'] [ASSIGN double_byte_character_string] */ +/* integer ->may be NULL ! */ +/* double_byte_character_string ->may be NULL ! */ +void *visit(double_byte_string_spec_c *symbol) { + s4o.print("WSTRING ["); + if (symbol->integer != NULL) + symbol->integer->accept(*this); + s4o.print("]"); + if (symbol->double_byte_character_string != NULL) { + s4o.print(" := "); + symbol->double_byte_character_string->accept(*this); + } + return NULL; +} + +/*| VAR [RETAIN|NON_RETAIN] incompl_located_var_decl_list END_VAR */ +/* option ->may be NULL ! */ +void *visit(incompl_located_var_declarations_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->incompl_located_var_decl_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* helper symbol for incompl_located_var_declarations */ +/*| incompl_located_var_decl_list incompl_located_var_decl ';' */ +void *visit(incompl_located_var_decl_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/* variable_name incompl_location ':' var_spec */ +void *visit(incompl_located_var_decl_c *symbol) { + symbol->variable_name->accept(*this); + s4o.print(" "); + symbol->incompl_location->accept(*this); + s4o.print(" : "); + symbol->var_spec->accept(*this); + return NULL; +} + + +/* AT incompl_location_token */ +void *visit(incompl_location_c *symbol) { + s4o.print(" AT "); + return print_token(symbol);; +} + + +/* intermediate helper symbol for: + * - non_retentive_var_decls + * - output_declarations + */ +/* | var_init_decl_list var_init_decl ';' */ +void *visit(var_init_decl_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + + +/***********************/ +/* B 1.5.1 - Functions */ +/***********************/ +void *visit(function_declaration_c *symbol) { + s4o.print("FUNCTION "); + symbol->derived_function_name->accept(*this); + s4o.print(" : "); + symbol->type_name->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->var_declarations_list->accept(*this); + s4o.print("\n"); + symbol->function_body->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "END_FUNCTION\n\n\n"); + return NULL; +} + +/* intermediate helper symbol for function_declaration */ +void *visit(var_declarations_list_c *symbol) {return print_list(symbol);} + +void *visit(function_var_decls_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("VAR "); + if (symbol->option != NULL) + symbol->option->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->decl_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_VAR\n"); + return NULL; +} + +/* intermediate helper symbol for function_var_decls */ +void *visit(var2_init_decl_list_c *symbol) { + print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); + return NULL; +} + +/*****************************/ +/* B 1.5.2 - Function Blocks */ +/*****************************/ +/* FUNCTION_BLOCK derived_function_block_name io_OR_other_var_declarations function_block_body END_FUNCTION_BLOCK */ +void *visit(function_block_declaration_c *symbol) { + s4o.print("FUNCTION_BLOCK "); + symbol->fblock_name->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->var_declarations->accept(*this); + s4o.print("\n"); + symbol->fblock_body->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "END_FUNCTION_BLOCK\n\n\n"); + return NULL; +} + +/* VAR_TEMP temp_var_decl_list END_VAR */ +void *visit(temp_var_decls_c *symbol) { + s4o.print("VAR_TEMP\n"); + s4o.indent_right(); + symbol->var_decl_list->accept(*this); + s4o.indent_left(); + s4o.print("END_VAR\n"); + return NULL; +} + +/* intermediate helper symbol for temp_var_decls */ +void *visit(temp_var_decls_list_c *symbol) {return print_list(symbol);} + +/* VAR NON_RETAIN var_init_decl_list END_VAR */ +void *visit(non_retentive_var_decls_c *symbol) { + s4o.print("VAR NON_RETAIN\n"); + s4o.indent_right(); + symbol->var_decl_list->accept(*this); + s4o.indent_left(); + s4o.print("END_VAR\n"); + return NULL; +} + + +/**********************/ +/* B 1.5.3 - Programs */ +/**********************/ +/* PROGRAM program_type_name program_var_declarations_list function_block_body END_PROGRAM */ +void *visit(program_declaration_c *symbol) { + s4o.print("PROGRAM "); + symbol->program_type_name->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + symbol->var_declarations->accept(*this); + s4o.print("\n"); + symbol->function_block_body->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "END_PROGRAM\n\n\n"); + return NULL; +} + + + +/********************************/ +/* B 1.7 Configuration elements */ +/********************************/ +/* +CONFIGURATION configuration_name + optional_global_var_declarations + (resource_declaration_list | single_resource_declaration) + optional_access_declarations + optional_instance_specific_initializations +END_CONFIGURATION +*/ +void *visit(configuration_declaration_c *symbol) { + s4o.print("CONFIGURATION "); + symbol->configuration_name->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + if (symbol->global_var_declarations != NULL) + symbol->global_var_declarations->accept(*this); + symbol->resource_declarations->accept(*this); + if (symbol->access_declarations != NULL) + symbol->access_declarations->accept(*this); + if (symbol->instance_specific_initializations != NULL) + symbol->instance_specific_initializations->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "END_CONFIGURATION\n\n\n"); + return NULL; +} + + +/* helper symbol for configuration_declaration */ +/*| resource_declaration_list resource_declaration */ +void *visit(resource_declaration_list_c *symbol) {return print_list(symbol);} + + +/* +RESOURCE resource_name ON resource_type_name + optional_global_var_declarations + single_resource_declaration +END_RESOURCE +*/ +void *visit(resource_declaration_c *symbol) { + s4o.print(s4o.indent_spaces + "RESOURCE "); + symbol->resource_name->accept(*this); + s4o.print(" ON "); + symbol->resource_type_name->accept(*this); + s4o.print("\n"); + s4o.indent_right(); + if (symbol->global_var_declarations != NULL) + symbol->global_var_declarations->accept(*this); + symbol->resource_declaration->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "END_RESOURCE\n"); + return NULL; +} + + +/* task_configuration_list program_configuration_list */ +void *visit(single_resource_declaration_c *symbol) { + symbol->task_configuration_list->accept(*this); + symbol->program_configuration_list->accept(*this); + return NULL; +} + +/* helper symbol for single_resource_declaration */ +/*| task_configuration_list task_configuration ';'*/ +void *visit(task_configuration_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + + +/* helper symbol for single_resource_declaration */ +/*| program_configuration_list program_configuration ';'*/ +void *visit(program_configuration_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + + +/* helper symbol for + * - access_path + * - instance_specific_init + */ +/* | any_fb_name_list any_identifier '.'*/ +void *visit(any_fb_name_list_c *symbol) {return print_list(symbol, ".", ".");} + + +/* [resource_name '.'] global_var_name ['.' structure_element_name] */ +void *visit(global_var_reference_c *symbol) { + if (symbol->resource_name != NULL) { + symbol->resource_name->accept(*this); + s4o.print("."); + } + symbol->global_var_name->accept(*this); + if (symbol->structure_element_name != NULL) { + s4o.print("."); + symbol->structure_element_name->accept(*this); + } + return NULL; +} + +/* program_name '.' symbolic_variable */ +void *visit(program_output_reference_c *symbol) { + symbol->program_name->accept(*this); + s4o.print("."); + symbol->symbolic_variable->accept(*this); + return NULL; +} + +/* TASK task_name task_initialization */ +void *visit(task_configuration_c *symbol) { + s4o.print("TASK "); + symbol->task_name->accept(*this); + s4o.print(" "); + symbol->task_initialization->accept(*this); + return NULL; +} + +/* '(' [SINGLE ASSIGN data_source ','] [INTERVAL ASSIGN data_source ','] PRIORITY ASSIGN integer ')' */ +void *visit(task_initialization_c *symbol) { + s4o.print("("); + if (symbol->single_data_source != NULL) { + s4o.print("SINGLE := "); + symbol->single_data_source->accept(*this); + s4o.print(", "); + } + if (symbol->interval_data_source != NULL) { + s4o.print("INTERVAL := "); + symbol->interval_data_source->accept(*this); + s4o.print(", "); + } + s4o.print("PRIORITY := "); + symbol->priority_data_source->accept(*this); + s4o.print(")"); + return NULL; +} + +/* PROGRAM [RETAIN | NON_RETAIN] program_name [WITH task_name] ':' program_type_name ['(' prog_conf_elements ')'] */ +void *visit(program_configuration_c *symbol) { + s4o.print("PROGRAM "); + if (symbol->retain_option != NULL) + symbol->retain_option->accept(*this); + symbol->program_name->accept(*this); + if (symbol->task_name != NULL) { + s4o.print(" WITH "); + symbol->task_name->accept(*this); + } + s4o.print(" : "); + symbol->program_type_name->accept(*this); + if (symbol->prog_conf_elements != NULL) { + s4o.print("("); + symbol->prog_conf_elements->accept(*this); + s4o.print(")"); + } + return NULL; +} + + +/* prog_conf_elements ',' prog_conf_element */ +void *visit(prog_conf_elements_c *symbol) {return print_list(symbol, "", ", ");} + +/* fb_name WITH task_name */ +void *visit(fb_task_c *symbol) { + symbol->fb_name->accept(*this); + s4o.print(" WITH "); + symbol->task_name->accept(*this); + return NULL; +} + +/* any_symbolic_variable ASSIGN prog_data_source */ +void *visit(prog_cnxn_assign_c *symbol) { + symbol->symbolic_variable->accept(*this); + s4o.print(" := "); + symbol->prog_data_source->accept(*this); + return NULL; +} + +/* any_symbolic_variable SENDTO data_sink */ +void *visit(prog_cnxn_sendto_c *symbol) { + symbol->symbolic_variable->accept(*this); + s4o.print(" => "); + symbol->prog_data_source->accept(*this); + return NULL; +} + +/* VAR_CONFIG instance_specific_init_list END_VAR */ +void *visit(instance_specific_initializations_c *symbol) { + s4o.print(s4o.indent_spaces + "VAR_CONFIG\n"); + s4o.indent_right(); + symbol->instance_specific_init_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "END_VAR\n"); + return NULL; +} + +/* helper symbol for instance_specific_initializations */ +/*| instance_specific_init_list instance_specific_init ';'*/ +void *visit(instance_specific_init_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/* resource_name '.' program_name '.' optional_fb_name_list '.' + ((variable_name [location] ':' located_var_spec_init) | (fb_name ':' fb_initialization)) +*/ +void *visit(instance_specific_init_c *symbol) { + symbol->resource_name->accept(*this); + s4o.print("."); + symbol->program_name->accept(*this); + symbol->any_fb_name_list->accept(*this); + if (symbol->variable_name != NULL) { + s4o.print("."); + symbol->variable_name->accept(*this); + } + if (symbol->location != NULL) { + s4o.print(" "); + symbol->location->accept(*this); + } + s4o.print(" : "); + symbol->initialization->accept(*this); + return NULL; +} + +/* helper symbol for instance_specific_init */ +/* function_block_type_name ':=' structure_initialization */ +void *visit(fb_initialization_c *symbol) { + symbol->function_block_type_name->accept(*this); + s4o.print(" := "); + symbol->structure_initialization->accept(*this); + return NULL; +} + + +/***********************************/ +/* B 2.1 Instructions and Operands */ +/***********************************/ +/*| instruction_list il_instruction */ +void *visit(instruction_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, "\n" + s4o.indent_spaces, "\n"); +} + +/* | label ':' [il_incomplete_instruction] eol_list */ +void *visit(il_instruction_c *symbol) { + if (symbol->label != NULL) { + symbol->label->accept(*this); + s4o.print(": "); + } + symbol->il_instruction->accept(*this); + return NULL; +} + + +/* | il_simple_operator [il_operand] */ +void *visit(il_simple_operation_c *symbol) { + symbol->il_simple_operator->accept(*this); + if (symbol->il_operand != NULL) + symbol->il_operand->accept(*this); + return NULL; +} + +/* | function_name [il_operand_list] */ +void *visit(il_function_call_c *symbol) { + symbol->function_name->accept(*this); + s4o.print(" "); + if (symbol->il_operand_list != NULL) + symbol->il_operand_list->accept(*this); + return NULL; +} + + +/* | il_expr_operator '(' [il_operand] eol_list [simple_instr_list] ')' */ +void *visit(il_expression_c *symbol) { + symbol->il_expr_operator->accept(*this); + s4o.print("("); + if (symbol->il_operand != NULL) + symbol->il_operand->accept(*this); + if (symbol->simple_instr_list != NULL) { + s4o.print("\n"); + s4o.indent_right(); + symbol->simple_instr_list->accept(*this); + s4o.indent_left(); + } + s4o.print(")"); + return NULL; +} + +/* il_jump_operator label */ +void *visit(il_jump_operation_c *symbol) { + symbol->il_jump_operator->accept(*this); + symbol->label->accept(*this); + return NULL; +} + +/* il_call_operator prev_declared_fb_name + * | il_call_operator prev_declared_fb_name '(' ')' + * | il_call_operator prev_declared_fb_name '(' eol_list ')' + * | il_call_operator prev_declared_fb_name '(' il_operand_list ')' + * | il_call_operator prev_declared_fb_name '(' eol_list il_param_list ')' + */ +void *visit(il_fb_call_c *symbol) { + symbol->il_call_operator->accept(*this); + symbol->fb_name->accept(*this); + s4o.print("("); + if (symbol->il_operand_list != NULL) + symbol->il_operand_list->accept(*this); + if (symbol->il_param_list != NULL) { + s4o.print("\n"); + s4o.indent_right(); + symbol->il_param_list->accept(*this); + s4o.indent_left(); + } + s4o.print(")"); + return NULL; +} + + +/* | function_name '(' eol_list [il_param_list] ')' */ +void *visit(il_formal_funct_call_c *symbol) { + symbol->function_name->accept(*this); + s4o.print("("); + if (symbol->il_param_list != NULL) { + s4o.print("\n"); + s4o.indent_right(); + symbol->il_param_list->accept(*this); + s4o.print(")"); + } + return NULL; +} + + +/* | il_operand_list ',' il_operand */ +void *visit(il_operand_list_c *symbol) {return print_list(symbol, "", ", ");} + +/* | simple_instr_list il_simple_instruction */ +void *visit(simple_instr_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, "\n" + s4o.indent_spaces, "\n" + s4o.indent_spaces); +} + +/* | il_initial_param_list il_param_instruction */ +void *visit(il_param_list_c *symbol) {return print_list(symbol);} + +/* il_assign_operator il_operand + * | il_assign_operator '(' eol_list simple_instr_list ')' + */ +void *visit(il_param_assignment_c *symbol) { + symbol->il_assign_operator->accept(*this); + s4o.print(" "); + if (symbol->il_operand != NULL) + symbol->il_operand->accept(*this); + if (symbol->simple_instr_list != NULL) { + s4o.print("(\n"); + s4o.indent_right(); + symbol->simple_instr_list->accept(*this); + s4o.indent_left(); + s4o.print(")"); + } + return NULL; +} + +/* il_assign_out_operator variable */ +void *visit(il_param_out_assignment_c *symbol) { + symbol->il_assign_out_operator->accept(*this); + symbol->variable->accept(*this); + return NULL; +} + + +/*******************/ +/* B 2.2 Operators */ +/*******************/ +void *visit(LD_operator_c *symbol) {s4o.print("LD "); return NULL;} +void *visit(LDN_operator_c *symbol) {s4o.print("LDN "); return NULL;} +void *visit(ST_operator_c *symbol) {s4o.print("ST "); return NULL;} +void *visit(STN_operator_c *symbol) {s4o.print("STN "); return NULL;} +void *visit(NOT_operator_c *symbol) {s4o.print("NOT "); return NULL;} +void *visit(S_operator_c *symbol) {s4o.print("S "); return NULL;} +void *visit(R_operator_c *symbol) {s4o.print("R "); return NULL;} +void *visit(S1_operator_c *symbol) {s4o.print("S1 "); return NULL;} +void *visit(R1_operator_c *symbol) {s4o.print("R1 "); return NULL;} +void *visit(CLK_operator_c *symbol) {s4o.print("CLK "); return NULL;} +void *visit(CU_operator_c *symbol) {s4o.print("CU "); return NULL;} +void *visit(CD_operator_c *symbol) {s4o.print("CD "); return NULL;} +void *visit(PV_operator_c *symbol) {s4o.print("PV "); return NULL;} +void *visit(IN_operator_c *symbol) {s4o.print("IN "); return NULL;} +void *visit(PT_operator_c *symbol) {s4o.print("PT "); return NULL;} +void *visit(AND_operator_c *symbol) {s4o.print("AND "); return NULL;} +void *visit(OR_operator_c *symbol) {s4o.print("OR "); return NULL;} +void *visit(XOR_operator_c *symbol) {s4o.print("XOR "); return NULL;} +void *visit(ANDN_operator_c *symbol) {s4o.print("ANDN "); return NULL;} +void *visit(ORN_operator_c *symbol) {s4o.print("ORN "); return NULL;} +void *visit(XORN_operator_c *symbol) {s4o.print("XORN "); return NULL;} +void *visit(ADD_operator_c *symbol) {s4o.print("ADD "); return NULL;} +void *visit(SUB_operator_c *symbol) {s4o.print("SUB "); return NULL;} +void *visit(MUL_operator_c *symbol) {s4o.print("MUL "); return NULL;} +void *visit(DIV_operator_c *symbol) {s4o.print("DIV "); return NULL;} +void *visit(MOD_operator_c *symbol) {s4o.print("MOD "); return NULL;} +void *visit(GT_operator_c *symbol) {s4o.print("GT "); return NULL;} +void *visit(GE_operator_c *symbol) {s4o.print("GE "); return NULL;} +void *visit(EQ_operator_c *symbol) {s4o.print("EQ "); return NULL;} +void *visit(LT_operator_c *symbol) {s4o.print("LT "); return NULL;} +void *visit(LE_operator_c *symbol) {s4o.print("LE "); return NULL;} +void *visit(NE_operator_c *symbol) {s4o.print("NE "); return NULL;} +void *visit(CAL_operator_c *symbol) {s4o.print("CAL "); return NULL;} +void *visit(CALC_operator_c *symbol) {s4o.print("CALC "); return NULL;} +void *visit(CALCN_operator_c *symbol) {s4o.print("CALCN "); return NULL;} +void *visit(RET_operator_c *symbol) {s4o.print("RET "); return NULL;} +void *visit(RETC_operator_c *symbol) {s4o.print("RETC "); return NULL;} +void *visit(RETCN_operator_c *symbol) {s4o.print("RETCN "); return NULL;} +void *visit(JMP_operator_c *symbol) {s4o.print("JMP "); return NULL;} +void *visit(JMPC_operator_c *symbol) {s4o.print("JMPC "); return NULL;} +void *visit(JMPCN_operator_c *symbol) {s4o.print("JMPCN "); return NULL;} + +/*| [NOT] any_identifier SENDTO */ +void *visit(il_assign_out_operator_c *symbol) { + if (symbol->option != NULL) + symbol->option->accept(*this); + symbol->variable_name->accept(*this); + s4o.print(" => "); + return NULL; +} + + +/***********************/ +/* B 3.1 - Expressions */ +/***********************/ +void *visit(or_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " OR ");} +void *visit(xor_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " XOR ");} +void *visit(and_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " AND ");} +void *visit(equ_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " = ");} +void *visit(notequ_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " <> ");} +void *visit(lt_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " < ");} +void *visit(gt_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " > ");} +void *visit(le_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " <= ");} +void *visit(ge_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " >= ");} +void *visit(add_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " + ");} +void *visit(sub_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " - ");} +void *visit(mul_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " * ");} +void *visit(div_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " / ");} +void *visit(mod_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " MOD ");} +void *visit(power_expression_c *symbol) {return print_binary_expression(symbol->l_exp, symbol->r_exp, " ** ");} +void *visit(neg_expression_c *symbol) {return print_unary_expression(symbol->exp, "-");} +void *visit(not_expression_c *symbol) {return print_unary_expression(symbol->exp, "NOT ");} + +void *visit(function_invocation_c *symbol) { + symbol->function_name->accept(*this); + s4o.print("("); + symbol->parameter_assignment_list->accept(*this); + s4o.print(")"); + return NULL; +} + +/********************/ +/* B 3.2 Statements */ +/********************/ +void *visit(statement_list_c *symbol) { + return print_list(symbol, s4o.indent_spaces, ";\n" + s4o.indent_spaces, ";\n"); +} + +/*********************************/ +/* B 3.2.1 Assignment Statements */ +/*********************************/ +void *visit( assignment_statement_c *symbol) { + symbol->l_exp->accept(*this); + s4o.print(" := "); + symbol->r_exp->accept(*this); + return NULL; +} + +/*****************************************/ +/* B 3.2.2 Subprogram Control Statements */ +/*****************************************/ +/* RETURN */ +void *visit(return_statement_c *symbol) { + s4o.print("RETURN"); + return NULL; +} + + +/* fb_name '(' [param_assignment_list] ')' */ +void *visit(fb_invocation_c *symbol) { + symbol->fb_name->accept(*this); + s4o.print("("); + if (symbol->param_assignment_list != NULL) + symbol->param_assignment_list->accept(*this); + s4o.print(")"); + return NULL; +} + + +/* helper symbol for fb_invocation */ +/* param_assignment_list ',' param_assignment */ +void *visit(param_assignment_list_c *symbol) {return print_list(symbol, "", ", ");} + + +void *visit(input_variable_param_assignment_c *symbol) { + symbol->variable_name->accept(*this); + s4o.print(" := "); + symbol->expression->accept(*this); + return NULL; +} + + +void *visit(output_variable_param_assignment_c *symbol) { + if (symbol->not_param != NULL) + symbol->not_param->accept(*this); + symbol->variable_name->accept(*this); + s4o.print(" => "); + symbol->variable->accept(*this); + return NULL; +} + + +void *visit(not_paramassign_c *symbol) { + s4o.print("NOT "); + return NULL; +} + + +/********************************/ +/* B 3.2.3 Selection Statements */ +/********************************/ +void *visit(if_statement_c *symbol) { + s4o.print("IF "); + symbol->expression->accept(*this); + s4o.print(" THEN\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + symbol->elseif_statement_list->accept(*this); + + if (symbol->else_statement_list != NULL) { + s4o.print(s4o.indent_spaces); s4o.print("ELSE\n"); + s4o.indent_right(); + symbol->else_statement_list->accept(*this); + s4o.indent_left(); + } + s4o.print(s4o.indent_spaces); s4o.print("END_IF"); + return NULL; +} + +/* helper symbol for if_statement */ +void *visit(elseif_statement_list_c *symbol) {return print_list(symbol);} + +/* helper symbol for elseif_statement_list */ +void *visit(elseif_statement_c *symbol) { + s4o.print(s4o.indent_spaces); s4o.print("ELSIF "); + symbol->expression->accept(*this); + s4o.print(s4o.indent_spaces); s4o.print("THEN\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + return NULL; +} + +void *visit(case_statement_c *symbol) { + s4o.print("CASE "); + symbol->expression->accept(*this); + s4o.print(" OF\n"); + s4o.indent_right(); + symbol->case_element_list->accept(*this); + if (symbol->statement_list != NULL) { + s4o.print(s4o.indent_spaces + "ELSE\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + } + s4o.indent_left(); + s4o.print(s4o.indent_spaces + "END_CASE"); + return NULL; +} + +/* helper symbol for case_statement */ +void *visit(case_element_list_c *symbol) { + return print_list(symbol); +} + +void *visit(case_element_c *symbol) { + s4o.print(s4o.indent_spaces); + symbol->case_list->accept(*this); + s4o.print(":\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + return NULL; +} + +void *visit(case_list_c *symbol) {return print_list(symbol, "", ", ");} + +/********************************/ +/* B 3.2.4 Iteration Statements */ +/********************************/ +void *visit(for_statement_c *symbol) { + s4o.print("FOR "); + symbol->control_variable->accept(*this); + s4o.print(" := "); + symbol->beg_expression->accept(*this); + s4o.print(" TO "); + symbol->end_expression->accept(*this); + if (symbol->by_expression != NULL) { + s4o.print(" BY "); + symbol->by_expression->accept(*this); + } + s4o.print(" DO\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_FOR"); + return NULL; +} + +void *visit(while_statement_c *symbol) { + s4o.print("WHILE "); + symbol->expression->accept(*this); + s4o.print(" DO\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("END_WHILE"); + return NULL; +} + +void *visit(repeat_statement_c *symbol) { + s4o.print("REPEAT\n"); + s4o.indent_right(); + symbol->statement_list->accept(*this); + s4o.indent_left(); + s4o.print(s4o.indent_spaces); s4o.print("UNTIL "); + symbol->expression->accept(*this); + s4o.print("\n" + s4o.indent_spaces + "END_REPEAT"); + return NULL; +} + +void *visit(exit_statement_c *symbol) { + s4o.print("EXIT"); + return NULL; +} + + +}; /* class generate_iec_c */ + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + + +visitor_c *new_code_generator(stage4out_c *s4o) {return new generate_iec_c(s4o);} +void delete_code_generator(visitor_c *code_generator) {delete code_generator;} + + + + + + + + + + + + + + + diff -r 000000000000 -r fb772792efd1 stage4/generate_iec/generate_iec.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/generate_iec/generate_iec.hh Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,55 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * Code to be included into the code generated by the 4th stage. + * + * This is part of the 4th stage that generates + * a ST and IL source program equivalent to the IL and ST + * code. + * This is expected to be used mainly in debuging the syntax + * and lexical parser, and as a starting point for other + * 4th stages. + */ + + + +/* + * GENERATE_IEC.HH + */ + + +#ifndef _GENERATE_IEC_HH +#define _GENERATE_IEC_HH + + + +#include +#include "../../absyntax/visitor.hh" + + + + +#endif /* _GENERATE_IEC_HH */ diff -r 000000000000 -r fb772792efd1 stage4/stage4.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/stage4.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,138 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * This file contains the code that stores the output generated + * by each specific version of the 4th stage. + */ + + + + + + +// #include /* required for NULL */ +#include +#include + + + + +#include "stage4.hh" + + + + + + +stage4out_c::stage4out_c(std::string indent_level) { + this->indent_level = indent_level; + this->indent_spaces = ""; +} + +stage4out_c::~stage4out_c(void) {} + + +void stage4out_c::indent_right(void) { + indent_spaces+=indent_level; +} + +void stage4out_c::indent_left(void) { + if (indent_spaces.length() >= indent_level.length()) + indent_spaces.erase(indent_spaces.length() - indent_level.length(), indent_level.length()); + else + indent_spaces.erase(); +} + + +void *stage4out_c::print(const char *str) { + std::cout << str; + return NULL; +} + + +void *stage4out_c::printupper(const char *str) { + for (int i = 0; str[i] != '\0'; i++) + std::cout << (unsigned char)toupper(str[i]); + return NULL; +} + + +void *stage4out_c::print(std::string str) { + std::cout << str; + return NULL; +} + + +void *stage4out_c::printupper(std::string str) { + /* The string standard class does not have a converter member function to upper case. + * We have to do it ourselves, a character at a time... + */ +#if 0 + /* The C++ way of doint things... */ + for (string::const_iterator p = str.begin(); p != str.end(); ++p) + std::cout << (unsigned char)toupper(*p); +#else + /* Or more simply... */ + printupper(str.c_str()); +#endif + return NULL; +} + + + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + + + +/* forward declarations... */ +/* These functions will be implemented in generate_XXX.cc */ +visitor_c *new_code_generator(stage4out_c *s4o); +void delete_code_generator(visitor_c *code_generator); + + +int stage4(symbol_c *tree_root) { + stage4out_c s4o; + visitor_c *generate_code = new_code_generator(&s4o); + + if (NULL == generate_code) + return -1; + + tree_root->accept(*generate_code); + + delete_code_generator(generate_code); + + return 0; +} + diff -r 000000000000 -r fb772792efd1 stage4/stage4.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stage4/stage4.hh Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,58 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * This file contains the code that stores the output generated + * by each specific version of the 4th stage. + */ + + +#include "../absyntax/absyntax.hh" + + +class stage4out_c { + public: + std::string indent_level; + std::string indent_spaces; + + public: + stage4out_c(std::string indent_level = " "); + ~stage4out_c(void); + + void indent_right(void); + void indent_left(void); + + void *print(const char *str); + void *print(std::string str); + + void *printupper(const char *str); + void *printupper(std::string str); +}; + + + + + +int stage4(symbol_c *tree_root); diff -r 000000000000 -r fb772792efd1 util/dsymtable.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/dsymtable.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,133 @@ +/* + * (c) 2005 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * A generic symbol table that allows duplicate values. + * + * This is used to create a symbol table of previously defined + * functions. Duplicate are allowed since the standard permits function\ + * overloading in the standard library. + */ + + +#include +#include "symtable.hh" + + + +/* A macro for printing out internal parser errors... */ +#define ERROR error_exit(__FILE__,__LINE__) +/* function defined in main.cc */ +extern void error_exit(const char *file_name, int line_no); + + + + + /* clear all entries... */ +template +void dsymtable_c::reset(void) { + _base.clear(); +} + + +template +void dsymtable_c::insert(const char *identifier_str, value_t new_value) { + // std::cout << "store_identifier(" << identifier_str << "): \n"; + std::pair new_element(identifier_str, new_value); + /* iterator res = */ _base.insert(new_element); +} + + +template +void dsymtable_c::insert(const symbol_c *symbol, value_t new_value) { + const token_c *name = dynamic_cast(symbol); + if (name == NULL) + ERROR; + insert(name->value, new_value); +} + + +#if 0 +template +void dsymtable_c::insert_noduplicate(const char *identifier_str, value_t new_value) { + if (find_value(identifier_str) != null_value) + /* already present in the set! */ + ERROR; + + // std::cout << "store_identifier(" << identifier_str << "): \n"; + std::pair new_element(identifier_str, new_value); + /* iterator res = */ _base.insert(new_element); +} + + +template +void dsymtable_c::insert_noduplicate(const symbol_c *symbol, value_t new_value) { + const token_c *name = dynamic_cast(symbol); + if (name == NULL) + ERROR; + insert_noduplicate(name->value, new_value); +} +#endif + + +/* returns null_value if not found! */ +template +value_type dsymtable_c::find_value(const char *identifier_str) { + iterator i = _base.find(identifier_str); + + if (i == _base.end()) + return null_value; + else + return i->second; +} + + +template +value_type dsymtable_c::find_value(const symbol_c *symbol) { + const token_c *name = dynamic_cast(symbol); + if (name == NULL) + ERROR; + return find_value(name->value); +} + + +/* debuging function... */ +template +void dsymtable_c::print(void) { + for(iterator i = _base.begin(); + i != _base.end(); + i++) + std::cout << i->second << ":" << i->first << "\n"; + std::cout << "=====================\n"; +} + + + + + + + + + diff -r 000000000000 -r fb772792efd1 util/dsymtable.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/dsymtable.hh Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,114 @@ +/* + * (c) 2005 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * A generic symbol table that allows duplicate values. + * + * This is used to create a symbol table of previously defined + * functions. Duplicate are allowed since the standard permits function\ + * overloading in the standard library. + */ + + + +#ifndef _DSYMTABLE_HH +#define _DSYMTABLE_HH + +#include "../absyntax/absyntax.hh" + +#include +#include + + + + +template class dsymtable_c { + /* Case insensitive string compare copied from + * "The C++ Programming Language" - 3rd Edition + * by Bjarne Stroustrup, ISBN 0201889544. + */ + class nocase_c { + public: + bool operator() (const std::string& x, const std::string& y) const { + std::string::const_iterator ix = x.begin(); + std::string::const_iterator iy = y.begin(); + + for(; (ix != x.end()) && (iy != y.end()) && (toupper(*ix) == toupper(*iy)); ++ix, ++iy); + if (ix == x.end()) return (iy != y.end()); + if (iy == y.end()) return false; + return (toupper(*ix) < toupper(*iy)); + }; + }; + + public: + typedef value_type value_t; + + private: + /* Comparison between identifiers must ignore case, therefore the use of nocase_c */ + typedef std::multimap base_t; + base_t _base; + + public: + typedef typename base_t::iterator iterator; + typedef typename base_t::const_iterator const_iterator; + typedef typename base_t::reverse_iterator reverse_iterator; + typedef typename base_t::const_reverse_iterator const_reverse_iterator; + + private: + void reset(void); /* clear all entries... */ + + public: + dsymtable_c(void) {}; + + void insert(const char *identifier_str, value_t value); + void insert(const symbol_c *symbol, value_t value); + + /* Search for an entry. Will return end_value() if not found */ + value_t end_value(void) {return null_value;} + value_t find_value(const char *identifier_str); + value_t find_value(const symbol_c *symbol); + + iterator find(const char *identifier_str) {return _base.find(identifier_str);} + + /* iterators pointing to beg/end of map... */ + iterator begin() {return _base.begin();} + const_iterator begin() const {return _base.begin();} + iterator end() {return _base.end();} + const_iterator end() const {return _base.end();} + reverse_iterator rbegin() {return _base.rbegin();} + const_reverse_iterator rbegin() const {return _base.rbegin();} + reverse_iterator rend() {return _base.rend();} + const_reverse_iterator rend() const {return _base.rend();} + + /* debuging function... */ + void print(void); +}; + + + +/* Templates must include the source into the code! */ +#include "dsymtable.cc" + +#endif /* _DSYMTABLE_HH */ diff -r 000000000000 -r fb772792efd1 util/symtable.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/symtable.cc Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,199 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * A generic symbol table. + * + * This is used to create symbol tables such as a list of + * variables currently in scope, etc... + * Note that the list of previously defined funstions uses the + * dsymtable_c instead, as it requires the table to store duplicate values. + */ + + +#include +#include "symtable.hh" + + + +/* A macro for printing out internal parser errors... */ +#define ERROR error_exit(__FILE__,__LINE__) +/* function defined in main.cc */ +extern void error_exit(const char *file_name, int line_no); + + + + +template +symtable_c::symtable_c(void) {inner_scope = NULL;} + + + /* clear all entries... */ +template +void symtable_c::reset(void) { + _base.clear(); +} + + /* create new inner scope */ +template +void symtable_c::push(void) { + if (inner_scope != NULL) { + inner_scope->push(); + } else { + inner_scope = new symtable_c(); + } +} + + /* clear most inner scope */ + /* returns 1 if this is the inner most scope */ + /* 0 otherwise */ +template +int symtable_c::pop(void) { + if (inner_scope != NULL) { + if (inner_scope->pop() == 1) { + delete inner_scope; + inner_scope = NULL; + } + return 0; + } else { + _base.clear(); + return 1; + } +} + +template +void symtable_c::set(const symbol_c *symbol, value_t new_value) { + if (inner_scope != NULL) { + inner_scope->set(symbol, new_value); + return; + } + + const token_c *name = dynamic_cast(symbol); + if (name == NULL) + ERROR; + set(name->value, new_value); +} + + +template +void symtable_c::set(const char *identifier_str, value_t new_value) { + if (inner_scope != NULL) { + inner_scope->set(identifier_str, new_value); + return; + } + + // std::cout << "set_identifier(" << identifier_str << "): \n"; + iterator i = _base.find(identifier_str); + if (i == _base.end()) + /* identifier not already in map! */ + ERROR; + + _base[identifier_str] = new_value; +} + +template +void symtable_c::insert(const char *identifier_str, value_t new_value) { + if (inner_scope != NULL) { + inner_scope->insert(identifier_str, new_value); + return; + } + + // std::cout << "store_identifier(" << identifier_str << "): \n"; + std::pair new_element(identifier_str, new_value); + std::pair res = _base.insert(new_element); + if (!res.second) + /* error inserting new identifier... */ + /* identifier already in map? */ + ERROR; +} + +template +void symtable_c::insert(const symbol_c *symbol, value_t new_value) { +/* +// not required... + if (inner_scope != NULL) { + inner_scope->insert(symbol, new_value); + return; + } +*/ + const token_c *name = dynamic_cast(symbol); + if (name == NULL) + ERROR; + insert(name->value, new_value); +} + + + +/* returns null_value if not found! */ +template +value_type symtable_c::find_value(const char *identifier_str) { + if (inner_scope != NULL) { + value_t token = inner_scope->find_value(identifier_str); + if (token != null_value) + /* found in the lower level */ + return token; + } + + /* if no lower level, or not found in lower level... */ + iterator i = _base.find(identifier_str); + + if (i == _base.end()) + return null_value; + else + return i->second; +} + + +template +value_type symtable_c::find_value(const symbol_c *symbol) { + const token_c *name = dynamic_cast(symbol); + if (name == NULL) + ERROR; + return find_value(name->value); +} + + +/* debuging function... */ +template +void symtable_c::print(void) { + for(iterator i = _base.begin(); + i != _base.end(); + i++) + std::cout << i->second << ":" << i->first << "\n"; + std::cout << "=====================\n"; + + if (inner_scope != NULL) { + inner_scope->print(); + } +} + + + + + + + + + diff -r 000000000000 -r fb772792efd1 util/symtable.hh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/symtable.hh Wed Jan 31 15:32:38 2007 +0100 @@ -0,0 +1,121 @@ +/* + * (c) 2003 Mario de Sousa + * + * Offered to the public under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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. + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +/* + * An IEC 61131-3 IL and ST compiler. + * + * Based on the + * FINAL DRAFT - IEC 61131-3, 2nd Ed. (2001-12-10) + * + */ + + +/* + * A generic symbol table. + * + * This is used to create symbol tables such as a list of + * variables currently in scope, a list of previously defined + * functions, etc... + */ + + + +#ifndef _SYMTABLE_HH +#define _SYMTABLE_HH + +#include "../absyntax/absyntax.hh" + +#include +#include + + + + +template class symtable_c { + /* Case insensitive string compare copied from + * "The C++ Programming Language" - 3rd Edition + * by Bjarne Stroustrup, ISBN 0201889544. + */ + class nocase_c { + public: + bool operator() (const std::string& x, const std::string& y) const { + std::string::const_iterator ix = x.begin(); + std::string::const_iterator iy = y.begin(); + + for(; (ix != x.end()) && (iy != y.end()) && (toupper(*ix) == toupper(*iy)); ++ix, ++iy); + if (ix == x.end()) return (iy != y.end()); + if (iy == y.end()) return false; + return (toupper(*ix) < toupper(*iy)); + }; + }; + + public: + typedef value_type value_t; + + private: + /* Comparison between identifiers must ignore case, therefore the use of nocase_c */ + typedef std::map base_t; + base_t _base; + + public: + typedef typename base_t::iterator iterator; + typedef typename base_t::const_iterator const_iterator; + typedef typename base_t::reverse_iterator reverse_iterator; + typedef typename base_t::const_reverse_iterator const_reverse_iterator; + + private: + /* pointer to symbol table of the next inner scope */ + symtable_c *inner_scope; + void reset(void); /* clear all entries... */ + + public: + symtable_c(void); + + void push(void); /* create new inner scope */ + int pop(void); /* clear most inner scope */ + + void set(const char *identifier_str, value_t value); + void set(const symbol_c *symbol, value_t value); + void insert(const char *identifier_str, value_t value); + void insert(const symbol_c *symbol, value_t value); + + /* Search for an entry. Will return end_value() if not found */ + value_t end_value(void) {return null_value;} + value_t find_value(const char *identifier_str); + value_t find_value(const symbol_c *symbol); + + iterator find(const char *identifier_str) {return _base.find(identifier_str);} + + /* iterators pointing to beg/end of map... */ + iterator begin() {return _base.begin();} + const_iterator begin() const {return _base.begin();} + iterator end() {return _base.end();} + const_iterator end() const {return _base.end();} + reverse_iterator rbegin() {return _base.rbegin();} + const_reverse_iterator rbegin() const {return _base.rbegin();} + reverse_iterator rend() {return _base.rend();} + const_reverse_iterator rend() const {return _base.rend();} + + /* debuging function... */ + void print(void); +}; + + + +/* Templates must include the source into the code! */ +#include "symtable.cc" + +#endif /* _SYMTABLE_HH */