/* 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; }