Menu

SVN-Code Commit Log


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

dotnet: answer the fglproto question in the README

Records what the prototype pass is actually for, since it is the first
thing anyone hits: for a single module it is redundant - the generator
infers return types from the bodies it can see, and the prototypes only
save a defensive As* conversion - but for anything multi-module it is
required, and a function returning several values is the case that
cannot be guessed at all.

Also adds the two things the new arithmetic probe turned up to the list
of mistakes the probes caught, and records DISPLAY of an unassigned
expression as a known gap rather than leaving it implied by what the
probe does not test.

2026-09-03 07:38:20 Tree
[r13073] by mikeaubury

dotnet: 4GL division does not truncate

Found while answering whether the C# backend still needs fglproto: a
cross-module test came out with c=3.00 where the C runtime said 3.50.

4GL division never truncates. 7/2 is 3.5 whatever the operands are
declared as, and it is the assignment afterwards that rounds or
truncates - LET i = 7/2 gives 3 because i is an INTEGER, not because
the division did anything. Checked against the C runtime:

dec 7/2 = 3.5000
flt 7/2 = 3.50
int 7/2 = 3
lit 7/2 = 3.5000
neg -7/2 = -3.5000

The generator emitted a bare C# "/", which truncates at the division
itself when both sides are integers, so a DECIMAL target held 3.00.
Division now goes through Fgl.Divide, which promotes first. Division by
zero returns NULL rather than throwing, as the C runtime does - a 4GL
program tests the result, it does not catch anything.

Two things fell out of fixing it:

assign_wrapper had no case for FLOAT or SMALLFLOAT, so assigning any
decimal-valued expression to one would not compile. It has one now,
the same shape as the CHAR, DECIMAL and SMALLINT cases.

DISPLAY of a MONEY lost its currency symbol. MONEY and DECIMAL are
both decimal? at runtime - that is deliberate, the difference is
presentational - so the type has to be named where it is still known,
and the generator now emits Fgl.DispMoney for it. The symbol comes
from DBMONEY, which can also place it after the number and change the
decimal separator, and it is extra to the declared width: a
MONEY(10,2) shows in 13 columns where a DECIMAL(10,2) shows in 12.

New end-to-end probe arith.4gl covers all of it: division into DECIMAL,
FLOAT, INTEGER and SMALLINT, negative and literal division, MONEY, and
the other three operators plus MOD.

Every result in it is assigned to a declared variable before being
displayed, deliberately. DISPLAYing a raw expression is a separate
question - an unassigned division has no declared scale, and 4GL prints
it at DECIMAL's maximum width - and that gap is recorded in the README
rather than quietly folded into this probe.

Fidelity: 27 semantics, 9 UI messages, 30 USING, the 66-line report and
6 end-to-end programs all agree with C. 203 runtime tests pass, d4 still
builds clean, and the standalone corpus is unchanged at 30 of 33.

2026-09-03 07:37:53 Tree
Older >