Menu

SVN-Code Commit Log


Commit Date  
[r13086] by mikeaubury

C# back end: report group breaks compile without an ORDER BY

ordGroup was declared inside "if (cnt)" - only when the report has ORDER BY
variables - but the three blocks below it use the variable unconditionally. A
report without an ORDER BY emitted the uses and not the declaration, and the
generated method would not compile. The declaration is hoisted out of the
guard; the comparison loop that needs the ORDER BY list stays inside it.

2026-09-05 12:32:36 Tree
[r13085] by mikeaubury

C# back end: escape identifiers that are C# keywords

A 4GL variable is emitted with an L_/M_/G_ prefix and cannot collide with a C#
keyword. A record field keeps the name its schema gave it, and "ref", "out",
"in", "object", "lock" and "event" are ordinary column names - those came out
as

public int? ref;
... x.ref ...

which will not compile.

bad_idents.c already had the mechanism for this and its word list was empty, so
it always answered "fine". That list feeds the parser's reject path, though, and
filling it would make the compiler refuse such programs the way the C back end
does for its own reserved words. C# has a proper escape, so this adds
LEXLIB_A4GL_cs_identifier alongside it: the name survives intact as @ref and
reads back as the same identifier.

Applied at the use site and at all four declaration sites - flat members,
nested records and arrays of records. A second helper handles the declaration
sites, which build the type and the name into one string, so the identifier to
escape is the last token rather than the whole thing.

2026-09-05 12:32:11 Tree
[r13084] by mikeaubury

C# back end: resolve decode_cmd_type locally

libLEX_CSNEW.so shipped with an unresolved symbol. decode_cmd_type is only
called when the generator reports an unreachable statement after a RETURN, so
the plug-in loaded normally and then died part way through a module with

libLEX_CSNEW.so: undefined symbol: decode_cmd_type

leaving a half written output file behind.

fgl_enums.c and fgl_enums.h are copied here from lex_esqlc rather than linked
from it: the lex plug-ins are built and shipped independently, so one must not
depend on another's objects. The enums come from the shared compiler headers,
so the two copies cannot drift apart silently - a new enumerator makes both
fail to build in the same way.

2026-09-05 12:31:55 Tree
[r13083] by mikeaubury

Bound the prototype parameter and return lists

add_bolton splits a prototype's parameter and return lists into

char *p_params[200];
char *p_rets[200];

with no bounds check, so a prototypes file listing more than 200 of either
walked off the end of the arrays and the compiler died with

*** stack smashing detected *** : terminated

A prototypes file is input data. No input should be able to smash the
compiler's stack, whatever is wrong with it. Both loops now stop at the limit
and say so:

Prototype for <name> has more than 199 return values - ignoring the rest

2026-09-05 12:31:40 Tree
[r13082] by mikeaubury

Coerce SMALLINT properly, and let make_cast be forced

ensure_smint tested for DTYPE_SMINT and then cast to DTYPE_INT. It was the only
one of the twelve ensure_* functions that casts to a different type from the one
it just tested for, so a value coerced for a SMALLINT parameter arrived as an
INTEGER. C did not care - the two interconvert silently - but a back end with a
distinct 16-bit type cannot pass the result to the parameter it was coerced for.

Also: 4GL says SMALLINT minus a literal is still SMALLINT, so no conversion was
requested. That holds for the 4GL type but not for the expression a back end
writes to evaluate it - C and C# both promote short arithmetic to int, and the
result has to be narrowed again before it can be passed to a SMALLINT
parameter. ensure_smint now asks for the cast on arithmetic, looking through
brackets so "(a - 1)" is treated like "a - 1".

make_cast took a "force" flag and never consulted it, so a cast that is a no-op
by 4GL type rules could not be requested even where a back end needs it written
out. It is now honoured, and ensure_smint uses it for the case above.

No change to the C back end's output on the example suite.

2026-09-05 12:31:28 Tree
[r13081] by mikeaubury

Fix INTERVAL YEAR TO MONTH arithmetic

Two bugs in the same function, both of which threw away the month part of a
year-month interval:

v1 = data_a[0] + data_a[1] / 12;

data_a is an int array, so dividing by an int truncated - every YEAR TO MONTH
value was treated as a whole number of years before the arithmetic ran. Fixed
by dividing by 12.0.

double yd, md;
int y = 0;
...
yd = floor (r1);
md = (r1 - y) * 12.0;

y is declared, initialised to zero and never assigned; the months are what is
left after the whole years, so it should have been yd. As written the month
field became the whole value times twelve.

Together these make subtraction correct where the result is positive and
smaller than the left operand. Addition, and a negative result, are still wrong
- the fault there is in how the result qualifier is encoded, which these two do
not reach. The interval fidelity probe records all three cases.

2026-09-05 12:30:53 Tree
[r13080] by mikeaubury

Fix variable substitution inside SQL subqueries

A 4GL variable used inside a subquery was never replaced by a placeholder and
never bound, so both the C and the C# back ends stopped with

Assertion failed: These should all have been removed by now... (sqlexpr.c)

and the statement could not be compiled at all.

make_list_item_list_from_select collects a statement's expressions so the back
ends can substitute variables for placeholders. Its E_SLI_IN_SELECT case walked
only complex_expr.left - the column being tested - and never the right, which is
the subquery. The E_SLI_SUBQUERY case below it does the right thing, but nothing
ever reached it, so nothing inside the subquery was collected.

The text writer already read both sides, so the collector was simply out of step
with it.

SELECT COUNT(*) INTO n FROM t WHERE k IN (SELECT a.k FROM u a WHERE a.c = v)

A constant in place of v compiled; a variable did not.

2026-09-05 12:30:37 Tree
[r13079] by mikeaubury

dotnet: qualify the builtins, so a program may reuse their names

Every builtin that can be static now lives on the static Fgl class and
is emitted as Fgl.X. Only the few that need the running session -
errorlog, err_get, lastkeypress and the SQLCA/flag members - stay
unqualified on FglModule.

The point is namespace: a builtin on the module base class reserves that
name across every 4GL program, so a program with its own function called
Current, or readln, or getuser, silently hides one. Adding Current in
r13077 collided with a member of that name in our own test example,
which was the warning. Since the generator decides how these are
written, the fix is to write them qualified rather than to hope the
names never clash - and Fgl.Pad, Fgl.Clipped and Fgl.Length were
already emitted that way, so this makes the surface consistent rather
than introducing a new convention.

Most of it went into map_fname's table, which is where a 4GL name is
turned into a .NET one; three sites emit their names directly and were
changed there. CURRENT is now one method taking an optional qualifier,
rather than a property and a separate CurrentQual.

The reservation that remains is deliberate and small. It is worth
knowing it exists: those few names are still spent.

On the large application this is 99 errors down to 94 - the qualifying
is not about the count, it is about what the generated code is allowed
to be. 203 runtime tests pass and the six end-to-end programs still
agree with the C runtime.

2026-09-04 20:16:33 Tree
[r13078] by mikeaubury

dotnet: BEFORE/AFTER INPUT, GET_FLDBUF, and variadic C-function stubs

164 compile errors down to 99 on the large application.

INPUT gained BEFORE INPUT and AFTER INPUT, which bracket the dialog as
BEFORE/AFTER ROW bracket a row, and GET_FLDBUF, which reads what the
user actually typed in a field before it was converted to the
variable's type. That last one is the point of it: a program uses
GET_FLDBUF precisely to look at input that would not convert.

The unimplemented client/server entry points now take params object?[]
rather than fixed arities. The arities were a guess and the guess was
wrong - the prototypes the generator has for these C functions carry no
signature at all, so the call sites vary and nothing here can predict
them. Taking whatever is passed is honest; they still throw.

That prototype gap is worth stating plainly, because it is not visible
in an error count: a C function declared with no signature generates a
call the compiler will accept and the program will get wrong. Reading
the real signatures out of the C source is the fix, and it has not been
done.

203 runtime tests pass; the six end-to-end programs and the UI message
stream still agree with the C runtime.

2026-09-04 20:07:24 Tree
[r13077] by mikeaubury

dotnet: DATETIME fetches, SQLCA as 4GL names it, and CURRENT

Continuing to work through what a large real application needs. This
takes it from 430 compile errors to 164.

DATETIME and INTERVAL fetches carry their qualifiers - 99 of the errors.
A DATETIME's precision is part of its type in 4GL, so the generator
emits the from/to units at every fetch, and ASqlResult had no overload
taking them.

SQLCA answers to its 4GL spelling. Generated code writes sqlca.sqlcode,
not SqlCode, so both are there now; sqlerrp is empty rather than
invented, since a managed driver has no routine name to report.

CURRENT comes in two shapes. Bare, it is a value - so a property, using
YEAR TO FRACTION(3), which is what a bare CURRENT means. With a
qualifier the generator passes the clause as text, and that form is now
CurrentQual, because a property and a method cannot share a name.

INT_FLAG and QUIT_FLAG are int? rather than int. They are INTEGER
variables in 4GL and generated code assigns AsInt(...) to them, which
is int?; declaring them int meant every such assignment wanted a cast
the generator does not emit.

One thing worth recording, because it will happen again: adding Current
to FglModule collided with a member of that name in the test example,
and would collide with any 4GL program that has its own. The example is
renamed, but every unqualified builtin added to the base class narrows
the space of names a 4GL program may use. It is the price of the
generator emitting these unqualified, and it argues for keeping that
surface no larger than it has to be.

203 runtime tests pass and all six end-to-end programs still agree with
the C runtime.

2026-09-04 20:04:52 Tree
Older >