diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..3806a3a --- /dev/null +++ b/src/io.c @@ -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; +}