Lab 4 : Type Checking and Semantic Analysis¶
The aim of this lab is to perform a semantic analysis of the internal form we built in the previous lab, especially type checking.
Checking static semantics¶
Static semantics concern things that a context-free grammar can’t handle. Examples of this include checking that:
Variables are declared this was actually already done for you in
parser.y
. Ideally it should have been done in this phase, since that is where it conceptually belongs. Why isn’t it?Operands have compatible types.
The number of arguments and their types at sub-routine calls are consistent with declarations.
All functions return something.
In this way we try to follow the semantic rules that have been stipulated for the language, for instance:
“If both operands of arithmetic operations such as addition, subtraction or multiplication are integers, the result should also be an integer.”
Type information¶
Information for type checking comes from:
the symbol table, where we store, for example, the type of a variable, a list of formal parameters for a procedure, what type a function returns, etc.
synthesised attributes, e.g. for arithmetic expressions. As operands of arithmetic operations may be arbitrary arithmetic expressions, we must synthesise information on their types. Where possible, this is done in the AST class constructors, but there are places where the type can’t be known at node creation time, and in these cases it has to be done during this phase.
In DIESEL we have to store, for example, the type of arithmetic
expressions so that type checking can be performed on expressions of
the form
(1 + 2) div (4 / 2)
(which actually results in a type conflict).
You can, of course, synthesise the
type information and throw it away when you have checked the
semantics, but then you may be forced to traverse the tree each time
you need to determine the type of an expression.
Type conversion¶
It may also be useful to allow the type checking to extend the AST. The
semantics in DIESEL allow us, for example, to add floating-point numbers
to integers. Since we did not concern ourselves with type checking while
constructing the AST, we are very likely to have partial trees like the
one shown on the left of the figure below, for example. The value in the
nodes’ type field has been written at each node. Even when doing type
checking we can see that we will have to change the integer in x to a
floating-point number before performing the addition. If we let the tree
stay as it is, we will have to take the addition’s type into
consideration when deciding how to generate quadruples for the
right-hand side child. Alternatively we have to know the types of the
operands when we generate quadruples for addition. To make quadruple
generation as simple as possible, we add a type translation node
(ast_cast
) which has a child of integer type but which is itself of
floating-point type. This is known as casting the integer number to real
type and is an example of type conversion. Since DIESEL is a rather
simple language with few data types, this is the only kind of type
conversion we will need to worry about. In a language which allows
pointers to objects (like C++), type casting and conversion can be a
whole science in itself. Anyway, after semantic analysis which includes
adding this node we get the tree on the right:
Quadruple generation for the modified tree will thus be simpler. All addition- and variable- nodes can now be translated to the same sequence of quadruples regardless of the context.
Type specifications for DIESEL¶
Here is the grammar (in reverse order) for DIESEL, with a description of
which type restrictions apply for each production (Note that the extra
error productions you were supposed to add in the previous lab are not
included here). We perform type checking one block at a time, by use of
a recursive call type_check()
which is passed from the root
downwards in the AST representing the block. You will need to keep in
mind which AST structures these productions correspond to (you should
have gained a good understanding of that in the previous lab), and
implement type checking and synthesizing where appropriate. Keep a copy
of parser.y
handy while reading this. The tokens generally appear as
their DIESEL symbols rather than their token names, e.g. (
instead of
T_LEFTPAR
, due to space considerations.
The numbers are just for ease of reference. bison
notation actually
groups several right-hand sides together under one left-hand side.
<id>
→T_IDENT
If the identifier is not declared, an error message is issued. The semantic action for this production can be found in
parser.y
, and has already been written. Ideally this check should take place at the same time as the rest of the type checking instead of at AST creation time. Why doesn’t it? The same comment applies for the following seven productions.<array_id>
→<id>
If the identifier is not declared as an array, an error message is issued.
<func_id>
→<id>
If the identifier is not declared as a function, an error message is issued.
<proc_id>
→<id>
If the identifier is not declared as a procedure, an error message is issued.
<rvar_id>
→<id>
If the identifier is not declared as a variable, parameter, or constant, an error message is issued. This production signifies any variable-like identifier that can appear on the right-hand side of an assignment statement.
<lvar_id>
→<id>
If the identifier is not declared as a variable or parameter, an error message is issued. This production signifies any variable-like identifier that can appear on the left-hand side of an assignment statement. Comparing it to the previous production we see that a constant can’t be assigned to, which makes sense. This duality is also reflected below in the productions for lvariable and rvariable, respectively.
<const_id>
→<id>
If the identifier is not declared as a constant, an error message is issued.
<type_id>
→<id>
If the identifier is not declared as a name type, an error message is issued.
<integer>
→T_INTNUM
<real>
→T_REALNUM
<func_call>
→<func_id>
(
<opt_expr_list>
)
The formal parameters (in the symbol table) must agree with the actual ones (in the
opt_expr_list
), both in type and number. The result must be the same type as the function’s return type.<factor>
→(
<expr>
)
<factor>
→NOT
<factor>
If the operand is not of integer type, an error message is issued. The result is of integer type.
<factor>
→<real>
<factor>
→<integer>
<factor>
→<func_call>
<factor>
→<rvariable>
<term>
→<factor>
<term>
→<term>
MOD
<factor>
Both operands must be of integer type. The result is of integer type.
<term>
→<term>
DIV
<factor>
Both operands must be of integer type. The result is of integer type.
<term>
→<term>
/
<factor>
The result is of real type. Both operands must be of real type or be transformed to it.
<term>
→<term>
*
<factor>
The result has the same type as both the operands, possibly after an operand has been transformed.
<term>
→<term>
AND
<factor>
Both operands must be of integer type. The result is of integer type.
<simple_expr>
→<term>
<simple_expr>
-
<term>
This is unary minus, as opposed to binary minus below.
<simple_expr>
→+
<term>
<simple_expr>
→<simple_expr>
-
<term>
The result has the same type as both the operands, possibly after an operand has been transformed.
<simple_expr>
→<simple_expr>
+
<term>
The result has the same type as both the operands, possibly after an operand has been transformed.
<simple_expr>
→<simple_expr>
OR
<term>
Both operands must be of integer type. The result is of integer type.
<expr>
→<simple_expr>
<expr>
→<expr>
>
<simple_expr>
The result is of integer type. Both operands have the same type, possibly after type transformation.
<expr>
→<expr>
<
<simple_expr>
The result is of integer type. Both operands have the same type, possibly after type transformation.
<expr>
→<expr>
<>
<simple_expr>
The result is of integer type. Both operands have the same type, possibly after type transformation.
<expr>
→<expr>
=
<simple_expr>
The result is of integer type. Both operands have the same type, possibly after type transformation.
<expr_list>
→<expr>
<expr_list>
→<expr_list>
,
<expr>
<opt_expr_list>
→<opt_expr_list>
→<expr_list>
<else_part>
→<else_part>
→ELSE
<stmt_list>
<elsif>
→ELSIF
<expr>
THEN
<stmt_list>
The predicate must be of integer type.
<elsif_list>
→<elsif_list>
→<elsif_list>
<elsif>
<rvariable>
→<rvar_id>
<rvariable>
→<array_id> ``[
<expr>
]
If the type of
<expr>
is different from the index type of<array_id>
, an error message is issued.<lvariable>
→<lvar_id>
<lvariable>
→<array_id>
[
<expr>
]
If the type of
<expr>
is different from the index type of<array_id>
, an error message is issued.<stmt>
→<stmt>
→RETURN
Can only be executed in procedures.
<stmt>
→RETURN
<expr>
Can only be executed in functions. The type of
<expr>
must agree with the type of the function.<stmt>
→<lvariable>
:=
<expr>
It is not allowed to assign a real expression to an integer variable. Real variables can, however, be assigned integer expressions after type transformation. Note the use of
lvariable
here.<stmt>
→<proc_id>
(
<opt_expr_list>
)
All actual parameters must agree in type and number with the formal parameters.
<stmt>
→WHILE
<expr>
DO
<stmt_list>
END
The predicate must be of integer type.
<stmt>
→IF
<expr>
THEN
<stmt_list>
<elsif_list>
<else_part>
END
The predicate must be of integer type.
<stmt_list>
→<stmt>
<stmt_list>
→<stmt_list>
;
<stmt>
<comp_stmt>
→BEGIN
<stmt_list>
END
<param>
→T_IDENT
:
<type_id>
<parm_list>
→<param>
<parm_list>
→<param_list>
;
<param>
<opt_param_list>
→<opt_param_list>
→(
<param_list>
)
<func_head>
→FUNCTION
T_IDENT
<proc_head>
→PROCEDURE
T_IDENT
<func_decl>
→<func_head>
<opt_param_list>
:
<type_id>
;
<const_part>
<variable_part>
<proc_decl>
→<proc_head>
<opt_param_list>
;
<const_part>
<variable_part>
<subprog_decl>
→<func_decl>
<subprog_part>
<comp_stmt>
;
A function must have at least one RETURN statement. This particular check has already been written in the skeleton program. Take a look at it.
<subprog_decl>
→<proc_decl>
<subprog_part>
<comp_stmt>
;
When this production is reduced, we have created an AST for one block, i.e., it is time to start type checking. This is already written, but currently commented out, in
parser.y
. Take a look.<subprog_decls>
→<subprog_decl>
<subprog_decls>
→<subprog_decls>
<subprog_decl>
<subprog_part>
→<subprog_part>
→<subprog_decls>
<var_decl>
→T_IDENT
:
<type_id>
;
<var_decl>
→T_IDENT
:
ARRAY
[
<integer>
]
OF
<type_id>
;
<var_decl>
→T_IDENT
:
ARRAY
[
<const_id>
]
OF
<type_id>
;
The index must be of integer type. This is already written for you.
<var_decls>
→<var_decl>
<var_decls>
→<var_decls>
<var_decl>
<variable_part>
→<variable_part>
→VAR
<var_decls>
<const_decl>
→T_IDENT
=
<integer>
;
<const_decl>
→T_IDENT
=
<real>
;
<const_decl>
→T_IDENT
=
T_STRINGCONST
;
<const_decl>
→T_IDENT
=
<const_id>
;
<const_decls>
→<const_decl>
<const_decls>
→<const_decls>
<const_decl>
<const_part>
→<const_part>
→CONST
<const_decls>
<proghead>
→PROGRAM
T_IDENT
<prog_decl>
→<proghead>
;
<const_part>
<variable_part>
<program>
→<prog_decl>
<subprog_part>
<comp_stmt>
.
Implementation¶
Your job is to implement type checking and semantic analysis by editing
the file semantic.cc
(and, in the quite likely case of your adding
internal methods, the file semantic.hh
as well).
In our implementation, type checking is done for one block at a time,
and is started by a call from parser.y
in the appropriate production
to do_typecheck()
in the semantic class. This call will perform some
block-specific initialisation, and then call the method type_check()
in the root node of the AST we are checking. This call will propagate
down through the AST and as nodes are passed, their types are checked
and their attributes are synthesized. The order of traversal is
important, since you obviously can’t synthesize information about a node
until you have information about its children.
The semantic class is a standalone class which is really only used as a
wrapper for type checking. It has a global instantiation, called
type_checker
, which is declared in semantic.cc
. The actual
type_check()
methods are part of the AST nodes, but their
implementations lie in the semantic.cc
file. This makes it easy to
modify the type checking implementation (by replacing this file with
another) without having to rewrite the entire AST class hierarchy.
Some of the methods have already been implemented; look at them to get
an idea of what you are supposed to do. Most of them you are to complete
yourself (look for Your code here
comments). In some cases it might be
convenient to add new methods to treat AST nodes which are very similar.
If you do so, you will also need to add your methods to the
semantic.hh
file.
Files that you have to change¶
- semantic.hh and semantic.cc
contain type-checking code implementation for the AST nodes as well as the declaration and implementation of the semantic class. These are the files you’re going to edit in this lab. They deal with type checking, type synthesizing, and parameter checking.
Other files of interest¶
All these files are the same as in lab 3:
- parser.y
is the input file to
bison
. This is the file you edited in the last lab; it contains the call todo_typecheck()
.- ast.hh
contains the definitions for the AST nodes.
- ast.cc
contains (part of) the implementations of the AST nodes.
- optimize.hh and optimize.cc
contain optimizing code. You won’t need to edit these files in this lab.
- quads.hh and quads.cc
contain quad generation code. You won’t need to edit these files in this lab.
- codegen.hh and codegen.cc
contain assembler generation code. You won’t need to edit these files until the last lab.
- error.hh, error.cc, symtab.hh, symbol.cc, symtab.cc, and scanner.l
use your versions from the earlier labs.
- main.cc
this is the compiler wrapper, parsing flags and the like.
- Makefile
and diesel use the same files as in the last lab.
Declarations and routines available in ast.hh, semantic.hh, and semantic.cc¶
The recursive type_check()
method common to all the AST classes takes
no arguments, and should return the type of the node it is called in.
The declarations can be found in ast.hh
. You will have to implement most
of them yourself.
The semantic class (currently) only contains two methods:
-
class
semantic
Public Functions
-
void
check_parameters
(ast_id*, ast_expr_list*) Compare formal vs. actual parameters in function/procedure calls.
It is also responsible for making sure that parameters are type-checked. You will have to write this method yourself, and also figure out from where to call it.
-
void
do_typecheck
(symbol *env, ast_stmt_list *body) Initiate type checking of a block of code.
Performs some initialisation before starting recursive type checking of a block.
The function has already been written for you.
- Parameters
env – the symbol representing the block to be checked (i.e., a function or procedure symbol)
body – an
ast_stmt_list
node representing the block body to be checked.
-
void
Program specification¶
Your program must synthesize type information correctly.
Your program must give error messages at semantic conflicts.
Your program must ensure that formal and actual parameters match.
Your program must make sure that functions return a value and that procedures do not.
Your program should extend the AST with type transformation nodes where appropriate.
Type checking should be done one block at a time. Apart from checking for undeclared or wrongly declared identifiers and the call to
do_typecheck()
, no type checking code is to reside inparser.y
.
Debugging help¶
As before, you can send all AST nodes to cout
. The types of
expressions are written out in the printout. Look at it to make sure
your nodes are given the correct type and that you add cast nodes in the
right places.
How do I know it’s correct?¶
Compare the AST before and after type checking has been performed. Study the node types and the cast nodes that were added.
Reporting your work¶
For the demonstration, run the test program for the two test cases
semtest1.d
andsemtest2.d
and discuss your code.Hand in your code as described in Handing in Results.
Example execution¶
Running make lab4
will take the sample input and compare it to the
sample output. You should test your code on more examples than this.
./diesel -b -p -f -y -a ../testpgm/semtest1.d
Symbol table will be printed after compilation.
An AST will be printed for each block.
No optimization will be done.
No quads will be generated.
Unoptimized AST for "INDEX"
Statement list (preceding, last_stmt)
+-Statement list (preceding, last_stmt)
| +-NULL
| +-Assignment (left, right)
| +-Id (J) [INTEGER]
| +-Integer [10]
+-While (condition, body)
+-Greater than (left, right) [INTEGER]
| +-Id (J) [INTEGER]
| +-Integer [0]
+-Statement list (preceding, last_stmt)
+-Statement list (preceding, last_stmt)
| +-Statement list (preceding, last_stmt)
| | +-NULL
| | +-Assignment (left, right)
| | +-Indexed (id, index)
| | | +-Id (I_ARR) [INTEGER]
| | | +-Sub (left, right) [INTEGER]
| | | +-Add (left, right) [INTEGER]
| | | | +-Integer [1]
| | | | +-Id (I) [INTEGER]
| | | +-Integer [1]
| | +-Id (J) [INTEGER]
| +-Assignment (left, right)
| +-Indexed (id, index)
| | +-Id (I_ARR) [INTEGER]
| | +-Indexed (id, index)
| | +-Id (I_ARR) [INTEGER]
| | +-Id (I) [INTEGER]
| +-Sub (left, right) [INTEGER]
| +-Indexed (id, index)
| | +-Id (I_ARR) [INTEGER]
| | +-Id (I) [INTEGER]
| +-Id (J) [INTEGER]
+-Assignment (left, right)
+-Id (J) [INTEGER]
+-Sub (left, right) [INTEGER]
+-Id (J) [INTEGER]
+-Integer [1]
Unoptimized AST for "MAX"
Statement list (preceding, last_stmt)
+-NULL
+-If (condition, then, elsif, else)
+-Greater than (left, right) [INTEGER]
| +-Id (A) [INTEGER]
| +-Id (B) [INTEGER]
+-Statement list (preceding, last_stmt)
| +-NULL
| +-Return (value)
| +-Id (A) [INTEGER]
+-Elsif list (preceding, last_elsif)
| +-NULL
| +-Elsif (condition, body)
| +-Greater than (left, right) [INTEGER]
| | +-Id (B) [INTEGER]
| | +-Id (A) [INTEGER]
| +-Statement list (preceding, last_stmt)
| +-NULL
| +-Return (value)
| +-Id (B) [INTEGER]
+-Statement list (preceding, last_stmt)
+-NULL
+-Return (value)
+-Id (A) [INTEGER]
Unoptimized AST for "DO_ZERO"
Statement list (preceding, last_stmt)
+-Statement list (preceding, last_stmt)
| +-NULL
| +-Assignment (left, right)
| +-Id (Z) [INTEGER]
| +-Id (ZERO) [INTEGER]
+-Procedure call (procedure, arguments)
+-Id (WRITE) [VOID]
+-Expression list (preceding, last_expr)
+-NULL
+-Id (Z) [INTEGER]
Unoptimized AST for "NASTY"
Statement list (preceding, last_stmt)
+-Statement list (preceding, last_stmt)
| +-Statement list (preceding, last_stmt)
| | +-Statement list (preceding, last_stmt)
| | | +-Statement list (preceding, last_stmt)
| | | | +-Statement list (preceding, last_stmt)
| | | | | +-Statement list (preceding, last_stmt)
| | | | | | +-NULL
| | | | | | +-Assignment (left, right)
| | | | | | +-Id (I) [INTEGER]
| | | | | | +-Unary minus (expr) [INTEGER]
| | | | | | +-Add (left, right) [INTEGER]
| | | | | | +-Integer [1]
| | | | | | +-Integer [3]
| | | | | +-Assignment (left, right)
| | | | | +-Id (I) [INTEGER]
| | | | | +-Id (J) [INTEGER]
| | | | +-Assignment (left, right)
| | | | +-Id (X) [REAL]
| | | | +-Cast [REAL]
| | | | +-Unary minus (expr) [INTEGER]
| | | | +-Id (I) [INTEGER]
| | | +-Assignment (left, right)
| | | +-Id (I) [INTEGER]
| | | +-Idiv (left, right) [INTEGER]
| | | +-Id (I) [INTEGER]
| | | +-Id (J) [INTEGER]
| | +-Assignment (left, right)
| | +-Id (X) [REAL]
| | +-Divide (left, right) [REAL]
| | +-Cast [REAL]
| | | +-Id (I) [INTEGER]
| | +-Cast [REAL]
| | +-Id (J) [INTEGER]
| +-Procedure call (procedure, arguments)
| +-Id (DO_ZERO) [VOID]
| +-NULL
+-If (condition, then, elsif, else)
+-And (left, right) [INTEGER]
| +-Equal (left, right) [INTEGER]
| | +-Id (X) [REAL]
| | +-Cast [REAL]
| | +-Id (I) [INTEGER]
| +-Less than (left, right) [INTEGER]
| +-Id (I) [INTEGER]
| +-Id (J) [INTEGER]
+-Statement list (preceding, last_stmt)
| +-Statement list (preceding, last_stmt)
| | +-Statement list (preceding, last_stmt)
| | | +-Statement list (preceding, last_stmt)
| | | | +-NULL
| | | | +-Assignment (left, right)
| | | | +-Id (I) [INTEGER]
| | | | +-Function call (function, arguments) [INTEGER]
| | | | +-Id (MAX) [INTEGER]
| | | | +-Expression list (preceding, last_expr)
| | | | +-Expression list (preceding, last_expr)
| | | | | +-NULL
| | | | | +-Id (I) [INTEGER]
| | | | +-Id (Y) [REAL]
| | | +-Assignment (left, right)
| | | +-Id (J) [INTEGER]
| | | +-Integer [0]
| | +-Assignment (left, right)
| | +-Id (X) [REAL]
| | +-Cast [REAL]
| | +-Integer [0]
| +-Assignment (left, right)
| +-Id (Y) [REAL]
| +-Cast [REAL]
| +-Integer [0]
+-NULL
+-NULL
Unoptimized AST for global level
Statement list (preceding, last_stmt)
+-Statement list (preceding, last_stmt)
| +-Statement list (preceding, last_stmt)
| | +-Statement list (preceding, last_stmt)
| | | +-Statement list (preceding, last_stmt)
| | | | +-Statement list (preceding, last_stmt)
| | | | | +-NULL
| | | | | +-Assignment (left, right)
| | | | | +-Id (A) [INTEGER]
| | | | | +-Integer [1]
| | | | +-Assignment (left, right)
| | | | +-Id (B) [INTEGER]
| | | | +-Integer [2]
| | | +-Assignment (left, right)
| | | +-Id (X) [REAL]
| | | +-Cast [REAL]
| | | +-Integer [1]
| | +-Assignment (left, right)
| | +-Id (Y) [REAL]
| | +-Real [3.14]
| +-Procedure call (procedure, arguments)
| +-Id (INDEX) [VOID]
| +-Expression list (preceding, last_expr)
| +-NULL
| +-Id (A) [INTEGER]
+-Procedure call (procedure, arguments)
+-Id (NASTY) [VOID]
+-Expression list (preceding, last_expr)
+-Expression list (preceding, last_expr)
| +-Expression list (preceding, last_expr)
| | +-Expression list (preceding, last_expr)
| | | +-NULL
| | | +-Idiv (left, right) [INTEGER]
| | | +-Integer [1]
| | | +-Integer [2]
| | +-Integer [5]
| +-Divide (left, right) [REAL]
| +-Cast [REAL]
| | +-Integer [1]
| +-Cast [REAL]
| +-Integer [2]
+-Add (left, right) [REAL]
+-Real [8]
+-Cast [REAL]
+-Integer [7]
7GLOBAL.4VOID7INTEGER4REAL4READ5WRITE7INT-ARG5TRUNC8REAL-ARG8SEMTEST11A1B1X1Y5I_ARR5INDEX1I1J3MAX5NASTY1I1J7NASTY_17NASTY_27DO_ZERO4OREZ4ZERO1Z
-----------------------------------------------------------------------------------------------------------------------------------------------^ (pool_pos = 143)
Symbol table (size = 31):
Pos Name Lev Hash Back Offs Type Tag
-----------------------------------------------
0: GLOBAL. 0 -1 159 0 GLOBAL. SYM_PROC lbl = -1 ar_size = 0
1: VOID 0 -1 82 0 VOID SYM_NAMETYPE
2: INTEGER 0 -1 462 0 VOID SYM_NAMETYPE
3: REAL 0 -1 324 0 VOID SYM_NAMETYPE
4: READ 0 -1 316 0 INTEGER SYM_FUNC lbl = 0 ar_size = 0
5: WRITE 0 -1 139 0 VOID SYM_PROC lbl = 1 ar_size = 0
6: INT-ARG 0 -1 210 0 INTEGER SYM_PARAM
7: TRUNC 0 -1 332 0 INTEGER SYM_FUNC lbl = 2 ar_size = 0
8: REAL-ARG 0 -1 427 0 REAL SYM_PARAM
9: SEMTEST1 0 -1 502 0 VOID SYM_PROC lbl = 3 ar_size = 112
10: A 1 -1 65 0 INTEGER SYM_VAR
11: B 1 -1 66 8 INTEGER SYM_VAR
12: X 1 -1 88 16 REAL SYM_VAR
13: Y 1 -1 89 24 REAL SYM_VAR
14: I_ARR 1 -1 45 32 INTEGER SYM_ARRAY card = 10
15: INDEX 1 -1 216 0 VOID SYM_PROC lbl = 4 ar_size = 8
16: I 2 -1 73 0 INTEGER SYM_PARAM
17: J 2 -1 74 0 INTEGER SYM_VAR
18: MAX 1 -1 70 0 INTEGER SYM_FUNC lbl = 5 ar_size = 0
19: A 2 -1 65 0 INTEGER SYM_PARAM
20: X 2 -1 88 8 REAL SYM_PARAM prec = A
21: NASTY 1 -1 47 0 VOID SYM_PROC lbl = 6 ar_size = 88
22: I 2 -1 73 0 INTEGER SYM_PARAM
23: J 2 -1 74 8 INTEGER SYM_PARAM prec = I
24: X 2 -1 88 16 REAL SYM_PARAM prec = J
25: Y 2 -1 89 24 REAL SYM_PARAM prec = X
26: NASTY_1 2 -1 95 0 INTEGER SYM_VAR
27: NASTY_2 2 -1 96 8 INTEGER SYM_ARRAY card = 10
28: DO_ZERO 2 -1 338 0 VOID SYM_PROC lbl = 7 ar_size = 8
29: OREZ 3 -1 0 0 INTEGER SYM_CONST value = 48
30: ZERO 3 -1 128 0 INTEGER SYM_CONST value = 48
31: Z 3 -1 90 0 INTEGER SYM_VAR
./diesel -b -p -f -y -a ../testpgm/semtest2.d
No optimization will be done.
Generating assembler for procedure "INDEX"
Type conflict, line 17, col 1: A function must return a value.
Type conflict, line 25, col 6: Can't assign a real value to an integer variable.
Type conflict, line 27, col 11: Type discrepancy between formal and actual parameters.
Type conflict, line 28, col 7: Can't assign a real value to an integer variable.
Type conflict, line 30, col 1: Procedures may not return a value.
Type conflict, line 34, col 7: More actual than formal parameters.
Type conflict, line 35, col 1: More formal than actual parameters.
Type conflict, line 36, col 7: Type discrepancy between formal and actual parameters.
Found 8 errors. Compilation aborted.
Note
Your col
value might differ slightly from the ones shown above
depending on how you implemented your column counter in your scanner.