Mon 16 Mar 11:09:06 CET 2026

This commit is contained in:
sbosse 2026-03-16 11:10:28 +01:00
parent 6279af71fb
commit acf58a5870

434
src/io.c Normal file
View File

@ -0,0 +1,434 @@
/*
IO
- code compiler for TINPUT, TOUTPUT
*/
#include "config.h"
#include "types.h"
#include "error.h"
#include "tokens.h"
#include "ops.h"
#include "mem.h"
#include "var.h"
#include "context.h"
#include "stack.h"
#include "reg.h"
#include "lexer.h"
#include "utils.h"
#include "code.h"
#include "printf.h"
#include "parsef.h"
#include "ccall.h"
#include "io.h"
#include "event.h"
#include "debug.h"
#include "str.h"
#include "vm.h"
/*
Code generator for input instruction ch?x,y,...
ioh: channel number
ioc: channel variable
ioc[0]='$': ch==data
*/
void CompileInput(lexer_t *L,reg_t *R,mem_t *M,index_t ioh,char *ioc) {
index_t argn=0;
address_t addr;
char iof = ','; // format
var_t *var;
token_t t;
char name[MAXNAMELEN+1];
do {
LookAheadToken(L);
switch (L->token) {
case '&':
// stdout stream handle number
SkipToken(L);
NextToken(L);
if (L->token!=TNUMBER) return error(R,ESYNTAX);
ioh=(index_t)L->x;
NextToken(L);
break;
case '#':
// format
SkipToken(L);
NextToken(L);
if (L->token!=TSTRING) return error(R,ESYNTAX);
iof=L->arg[0];
NextToken(L);
break;
case TIDENTIFIER: // could be x() or x$, must be handled in advance!
string_copy(name,L->name,0);
SkipToken(L);
LookAheadToken(L);
switch (L->token) {
case TDOLLAR:
// String var, either allocated here (default length) or already allocated
addr=Allocate(M,R, name, MSTRING, 0, &var);
if (!addr) return error(R,EVAR);
CODEADD1A(M,PUSHD,var->length);
CODEADD1A(M,PUSHD,addr);
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
CODEADD0(M,INSTRING);
NextToken(L);
break;
case TLPAR:
// array?
addr=Allocate(M,R, name, MVAR, 1, &var);
if (!addr) return error(R,EVAR);
NextToken(L);
LookAheadToken(L);
argn=0;
if (L->token!=TRPAR) do {
L->ps=PARGS; NextToken(L); CompileExpr(L, M, R);
argn++;
} while (L->token==TNEXTARG);
if (L->token!=TRPAR) return error(R,ESYNTAX);
NextToken(L);
if (argn==0) {
// read entire array (or only one element if scalar var)
CODEADD1A(M,PUSHD,1);
CODEADD1A(M,PUSHD,var->length);
} else {
// TODO range check
}
CODEADD1A(M,PUSHD,addr);
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
// [ch,addr,b,a]
CODEADD0(M,INARRAY);
break;
default:
// scalar variable?
addr=Allocate(M,R, name, MVAR, 1, NULL);
CODEADD1A(M,PUSHD,addr); // var address
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
// [ch,addr]
CODEADD0(M,INNUMBER);
break;
}
NextToken(L);
break;
}
} while (L->token==TNEXTARG);
}
/*
Code generator for output instruction ch!x,y,...
ioh: channel number
ioc: channel variable
*/
void CompileOutput(lexer_t *L,reg_t *R,mem_t *M,index_t ioh,char *ioc) {
index_t argn=0;
address_t addr;
char iof = ' '; // format
var_t *var;
token_t t;
char *input;
address_t pc1,pc2,pc3;
do {
LookAheadToken(L);
switch (L->token) {
case '&':
// stdout stream handle number
SkipToken(L);
NextToken(L);
if (L->token!=TNUMBER) return error(R,ESYNTAX);
ioh=(index_t)L->x;
NextToken(L);
break;
case '#':
// format
SkipToken(L);
NextToken(L);
if (L->token!=TSTRING) return error(R,ESYNTAX);
if (L->arglen>0) iof=L->arg[0];
else iof=0;
NextToken(L);
break;
case TIDENTIFIER: // could be x() or x$, must be handled in advance!
input=L->input; // save possible start of expression
addr=Allocate(M,R, L->name, MANY, 1, &var);
#if HAS_FUNC > 0
if (!addr && L->level) {
string_rconcat(L->name,L->fun); // function parameter?
addr=Allocate(M,R, L->name, MANY, 1, &var);
}
#endif
SkipToken(L);
LookAheadToken(L);
if (!addr) return error(R,EVAR); // variale must be already defined
switch (L->token) {
case TLPAR:
// function call or array variable, or expression with...
if (var->type==MVAR) {
// scalar or array variable
// a() and a(start,end) must be handled here
// a(index) is handled in expression mode
// TODO: remove this microprogram, replace with OUTARRAY op!!!
NextToken(L);
LookAheadToken(L);
argn=0;
if (L->token!=TRPAR) do {
L->ps=PARGS; NextToken(L); CompileExpr(L, M, R);
argn++;
} while (L->token==TNEXTARG);
if (L->token!=TRPAR) return error(R,ESYNTAX);
SkipToken(L);
switch (argn) {
case 0:
CODEADD1N(M,PUSHD,1);
CODEADD1N(M,PUSHD,var->length);
// full array output
SkipToken(L);
case 2:
// array range output
// range is on stack (a,b) --
// TODO range check
CODEADD1I(M,TOF,2);
CODEADD1N(M,PUSHF,(number_t) 1);
pc1=M->bottom; CODEADD1N(M,PUSHF,(number_t) 0);
pc2=M->bottom; CODEADD1N(M,PUSHF,(number_t) 0);
// (i=a,b,1,loop start,loop end)
CODESTORE1N(M,pc1,PUSHF,(number_t)M->bottom);
CODEADD1N(M,PUSHD,addr);
CODEADD1I(M,NTHF,5);
CODEADD1N(M,PUSHD,1);
CODEADD0(M,SUB);
CODEADD1N(M,PUSHD,number_s);
CODEADD0(M,MUL);
CODEADD0(M,ADD);
CODEADD1I(M,READ,-1);
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
CODEADD0(M,OUTNUMBER);
if (iof) {
CODEADD1I(M,NTHF,5);
CODEADD1I(M,NTHF,4);
CODEADD0(M,EQ);
pc3=M->bottom; CODEADD1A(M,JNZ,0);
CODEADD1I(M,PUSHD,iof);
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
CODEADD0(M,OUTCHAR);
CODESTORE1A(M,pc3,JNZ,M->bottom);
}
CODEADD0(M,LOOP);
CODESTORE1N(M,pc2,PUSHF,(number_t)M->bottom);
NextToken(L);
argn++;
goto outputsep;
case 1:
// array index expression, index on stack
// TODO range check
CODEADD1N(M,PUSHD,number_s);
CODEADD0(M,MUL);
CODEADD1N(M,PUSHD,addr-number_s);
CODEADD0(M,ADD);
CODEADD1I(M,READ,-1);
}
}
break;
case '$':
// string variable TODO
SkipToken(L);
if (var->type!=MSTRING) return error(R,EVAR);
CODEADD1N(M,PUSHD,addr);
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
CODEADD0(M,OUTSTRING);
NextToken(L);
argn++;
goto outputsep;
break;
default:
// restore lexer, identifier is not consumed
L->input=input;
}
L->token=TIDENTIFIER;
case TCHAR:
case TDOLLAR:
case TEVENT:
case TLPAR:
case TNUMBER:
case TSTRING:
case TCCALL:
// start of an expression or single value?
t=L->token;
// an expression
// we must know which type the expression has to choose OUTXX,
// feedback from CompileExpr
L->ps=PEXPR; NextToken(L); CompileExpr(L, M, R); if (R->error) return;
// assuming type number
switch (L->type) {
case TCHAR:
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
CODEADD0(M,OUTCHAR);
break;
case TNUMBER:
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
CODEADD0(M,OUTNUMBER);
break;
case TEVENT: // event name string
case TSTRING:
// string
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh); // channel
CODEADD0(M,OUTSTRING);
break;
}
argn++;
if (L->token==TRPAR) NextToken(L);
break;
default:
return error(R,ESYNTAX);
}
outputsep:
if (argn && L->token==TNEXTARG && iof) {
CODEADD1I(M,PUSHD,iof);
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh);
CODEADD0(M,OUTCHAR);
}
} while (L->token==TNEXTARG);
if (L->token==';') {
// no newline
SkipToken(L);
} else {
// newline
CODEADD1I(M,PUSHD,(int)'\n');
if (ioc[0]) CODEADDREADVAR(M,R,ioc) else CODEADD1I(M,PUSHD,ioh);
CODEADD0(M,OUTCHAR);
}
}
/*
VM execution loop plug-in for IO operations
Returns: 1 success, 0 error, -1 suspended
*/
int RunIO(context_t *C,op_t * op) {
stack_t *DS=C->DS;
stack_t *FS=C->FS;
mem_t *M=C->M;
reg_t *R=C->R;
index_t sp=C->DS->sp;
number_t ax;
index_t ix,ia,ib,ic,io,status;
address_t addr,saddr;
switch (op->command) {
case INARRAY:
// load data into array variable (raneg [a,b])
STACKUNDERFLOW(DS,4,R);
io=(index_t)POP(DS); addr=(address_t)POP(DS);
ib=(index_t)POP(DS); ia=(index_t)POP(DS);
addr+=(number_s*(ia-1));
if (io==-1) {
// DATA
for(ix=ia;ix<=ib;ix++) {
if (!R->data || M->data[R->data]!='N') { R->error=EDATA; return 0; };
R->data++;
ax=NUMBER(M,R->data);
NUMBER(M,addr)=ax;
R->data+=number_s;
addr+=number_s;
}
} else {
// TODO read from channel
ic=parse_stdin(io);
for(ix=ia;ix<=ib;ix++) {
status = ReadNumber(&ax);
if (status<0) { R->error=EINPUT; return 0; }
else if (status==0) {
// only allowed if first element, otherwise an error
if (ix==ia) {
// blocked IO
// rewind sp/pc
parse_stdin(ic);
C->DS->sp=sp;
DECR(R->pc,ops_s[op->command]+1);
Suspend(C,EVIO,io,0);
return -1;
} else { R->error=EINPUT; return 0; }
}
NUMBER(M,addr)=ax;
addr+=number_s;
}
}
break;
case INNUMBER:
STACKUNDERFLOW(DS,2,R);
io=(index_t)POP(DS); addr=(address_t)POP(DS);
if (io==-1) {
// DATA
if (!R->data || M->data[R->data]!='N') { R->error=EDATA; return 0; };
R->data++;
ax=NUMBER(M,R->data);
R->data+=number_s;
NUMBER(M,addr)=ax;
} else {
// Read from STDIN or port channel
// ReadNumber==0: blocked, ReadNumber<0: Error, ReadNumber>0: okay
ic=parse_stdin(io);
status=ReadNumber(&ax);
if (status<0) { R->error=EINPUT; return 0; }
else if (status==0) {
// blocked IO
// rewind sp/pc
parse_stdin(ic);
C->DS->sp=sp;
DECR(R->pc,ops_s[op->command]+1);
Suspend(C,EVIO,io,0);
return -1;
}
NUMBER(M,addr)=ax;
parse_stdin(ic);
}
break;
case INSTRING:
STACKUNDERFLOW(DS,2,R);
ix=(index_t)POP(DS); addr=(address_t)POP(DS);
ia=(index_t)POP(DS); // max length of string variable
if (ix==-1) {
// DATA
if (!R->data || M->data[R->data]!='S') { R->error=EDATA; return 0; };
R->data++;
// length of string
ib=NUMBER(M,R->data)-1;
R->data+=index_s;
if (ib>ia) ib=ia;
for(;ib>=0;ib--,addr++,R->data++) {
M->data[addr]=M->data[R->data];
}
} else {
// TODO read from channel
R->error=ETODO; return 0;
}
break;
// IO: Output
// TODO: check output channel blocking, AVAILBYTE(od)!=0,
// else restore stack with updated parameter (value, offset, length, ..) and Suspend(C,EVIO,io,0);
case OUTCHAR:
STACKUNDERFLOW(DS,2,R);
ix=(index_t)POP(DS); ax=POP(DS); ix=print_stdout(ix); print_char((int)ax); print_stdout(ix);
break;
case OUTNUMBER:
STACKUNDERFLOW(DS,2,R);
ix=(index_t)POP(DS); ax=POP(DS); ix=print_stdout(ix); print_int((int)ax); print_stdout(ix);
break;
case OUTSTRING:
STACKUNDERFLOW(DS,2,R);
ix=(index_t)POP(DS); addr=POP(DS); ix=print_stdout(ix); print_string(&M->data[addr]); print_stdout(ix);
break;
case OUTARRAY:
// ( addr len od -- )
STACKUNDERFLOW(DS,3,R);
ix=(index_t)POP(DS); ib=(index_t)POP(DS); addr=POP(DS); ix=print_stdout(ix); print_array((number_t *)&M->data[addr],ib); print_stdout(ix);
break;
}
return 1;
}