Mon 16 Mar 11:09:06 CET 2026
This commit is contained in:
parent
6279af71fb
commit
acf58a5870
434
src/io.c
Normal file
434
src/io.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user