Menu

SVN-Code Commit Log


Commit Date  
[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
[r13076] by mikeaubury

lex_cs/runtime: the 4GL builtins the generator emits unqualified

Running a large real 4GL application through the C# generator turned up
33 names the generated code calls that nothing defined. This adds them,
in a new lib/liblex/lex_cs/runtime - beside the generator that emits
them rather than off in the runtime tree, since the two have to agree.

They compile into Aubit4GL.Runtime rather than forming an assembly of
their own, and FglModule is now partial to allow it. That is forced
rather than chosen: generated code calls these unqualified, so they
have to be members of the class it inherits, and a separate assembly
could not provide them.

FglStrings GetSubstring, WriteSubstring, SwitchTrim
FglEnvironment FGL_GETENV, NUM_ARGS, ARG_VAL, RUN, ERRORLOG, and the
file helpers
FglProcess getuser, get_pid, get_envvar, err_get, lastkeypress

The substring pair carries the semantics that are easy to get wrong:
4GL substrings are 1-based and inclusive at both ends, a range running
off the end returns what there is rather than failing, and a write pads
the target with blanks and pads or truncates the value to the width of
the range, so the string's length never changes. Those rules came from
the older C# runtime in cs/, which had them right; they are not
invented here.

The client/server call layer - create_named_socket, connect_to_server,
push_server_call_arg and the rest - is deliberately NOT implemented.
It is the client half of a socket protocol whose server side is not in
this tree, so it cannot be written blind, and a stub that returned
success would give a program that appears to run and silently never
talks to its server. They throw with an explanation instead.

Also fixes ASqlReportError, which was mine: r13063 retargeted
IfxReportError to ASqlSetError everywhere except the report path, which
kept emitting a name nothing defines. 93 of the errors were that.

And the end-to-end probe now builds the runtime once before its loop.
Without that the first program in it absorbs a cold restore and can be
reported as "the generated C# did not build" - a failure that has
nothing to do with the program and disappears on the next run. It cost
me a while to trust it was not a real regression, which is exactly the
kind of doubt a test suite must not create.

Fidelity: 27 semantics, 9 UI messages, 30 USING, the 66-line report and
6 end-to-end programs all still agree with C, from a cold build cache.
203 runtime tests pass.

2026-09-04 19:52:29 Tree
[r13075] by mikeaubury

dotnet: fglproto takes module names, and it is already documented

I had the README telling people to pass .dat files and to filter the
.glb out of the glob. That works, but it is not the documented form and
it is more awkward than it needs to be: fglproto takes the module NAME,
appends .dat itself, and reads the module definition the WRITE pass
left. Naming modules rather than files also makes the .glb problem
disappear - a globals file is not a module, so it never appears in the
list in the first place.

Verified both forms produce the same prototypes.unl, on a two-module
toy and on the eight-module d4 program (45 prototypes either way).

Also says where this comes from, since it is reasonable to wonder. The
sequence is not something the C# back end invented: docs/web_services.txt
has documented fglproto since long before there was one - it is how
exported function stubs are generated for the SOAP support - and it
gives the same two steps, 4glpc -t WRITE per module then fglproto over
them all. The C# back end just consumes the same prototypes.unl.

The end-to-end probe was already using the documented form; only the
README prose was off.

2026-09-04 14:53:25 Tree
Older >