6357 lines
146 KiB
C
6357 lines
146 KiB
C
/****************************************************************************
|
|
* apps/interpreters/bas/var.c
|
|
*
|
|
* Copyright (c) 1999-2014 Michael Haardt
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Adapted to NuttX and re-released under a 3-clause BSD license:
|
|
*
|
|
* Copyright (C) 2014 Gregory Nutt. All rights reserved.
|
|
* Authors: Alan Carvalho de Assis <Alan Carvalho de Assis>
|
|
* Gregory Nutt <gnutt@nuttx.org>
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* 3. Neither the name NuttX nor the names of its contributors may be
|
|
* used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "statement.h"
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
#define _(String) String
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
struct Value *stmt_CALL(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGPROCIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE)
|
|
{
|
|
if (func(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pass == COMPILE)
|
|
{
|
|
if (Global_find
|
|
(&globals, pc.token->u.identifier,
|
|
(pc.token + 1)->type == T_OP) == 0)
|
|
{
|
|
return Value_new_ERROR(value, UNDECLARED);
|
|
}
|
|
}
|
|
|
|
if (pc.token->u.identifier->sym->type != USERFUNCTION &&
|
|
pc.token->u.identifier->sym->type != BUILTINFUNCTION)
|
|
{
|
|
return Value_new_ERROR(value, TYPEMISMATCH1, "variable", "function");
|
|
}
|
|
|
|
func(value);
|
|
if (Value_retype(value, V_VOID)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_CASE(struct Value *value)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Pc *selectcase, *nextcasevalue;
|
|
|
|
if ((selectcase = findLabel(L_SELECTCASE)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYCASE);
|
|
}
|
|
|
|
for (nextcasevalue = &selectcase->token->u.selectcase->nextcasevalue;
|
|
nextcasevalue->line != -1;
|
|
nextcasevalue = &nextcasevalue->token->u.casevalue->nextcasevalue);
|
|
|
|
*nextcasevalue = pc;
|
|
if (pass == COMPILE)
|
|
{
|
|
pc.token->u.casevalue->endselect =
|
|
selectcase->token->u.selectcase->endselect;
|
|
}
|
|
|
|
pc.token->u.casevalue->nextcasevalue.line = -1;
|
|
++pc.token;
|
|
switch (statementpc.token->type)
|
|
{
|
|
case T_CASEELSE:
|
|
break;
|
|
|
|
case T_CASEVALUE:
|
|
{
|
|
struct Pc exprpc;
|
|
|
|
do
|
|
{
|
|
if (pc.token->type == T_IS)
|
|
{
|
|
++pc.token;
|
|
switch (pc.token->type)
|
|
{
|
|
case T_LT:
|
|
case T_LE:
|
|
case T_EQ:
|
|
case T_GE:
|
|
case T_GT:
|
|
case T_NE:
|
|
break;
|
|
|
|
default:
|
|
return Value_new_ERROR(value, MISSINGRELOP);
|
|
}
|
|
|
|
++pc.token;
|
|
exprpc = pc;
|
|
if (eval(value, "`is'")->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (Value_retype
|
|
(value,
|
|
selectcase->token->u.selectcase->type)->type ==
|
|
V_ERROR)
|
|
{
|
|
pc = exprpc;
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
|
|
else /* value or range */
|
|
{
|
|
exprpc = pc;
|
|
if (eval(value, "`case'")->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (Value_retype
|
|
(value,
|
|
selectcase->token->u.selectcase->type)->type ==
|
|
V_ERROR)
|
|
{
|
|
pc = exprpc;
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_TO)
|
|
{
|
|
++pc.token;
|
|
exprpc = pc;
|
|
if (eval(value, "`case'")->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (Value_retype
|
|
(value,
|
|
selectcase->token->u.selectcase->type)->type ==
|
|
V_ERROR)
|
|
{
|
|
pc = exprpc;
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while (1);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pc = pc.token->u.casevalue->endselect;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_CHDIR_MKDIR(struct Value *value)
|
|
{
|
|
int res = -1, err = -1;
|
|
struct Pc dirpc;
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
dirpc = pc;
|
|
if (eval(value, _("directory"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
switch (statementpc.token->type)
|
|
{
|
|
case T_CHDIR:
|
|
res = chdir(value->u.string.character);
|
|
break;
|
|
|
|
case T_MKDIR:
|
|
res = mkdir(value->u.string.character, 0777);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
err = errno;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && res == -1)
|
|
{
|
|
pc = dirpc;
|
|
return Value_new_ERROR(value, IOERROR, strerror(err));
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_CLEAR(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
Global_clear(&globals);
|
|
FS_closefiles();
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_CLOSE(struct Value *value)
|
|
{
|
|
int hasargs = 0;
|
|
struct Pc chnpc;
|
|
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
chnpc = pc;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
hasargs = 1;
|
|
++pc.token;
|
|
}
|
|
|
|
if (eval(value, (const char *)0) == (struct Value *)0)
|
|
{
|
|
if (hasargs)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGEXPR, _("channel"));
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
hasargs = 1;
|
|
if (value->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET && FS_close(value->u.integer) == -1)
|
|
{
|
|
Value_destroy(value);
|
|
pc = chnpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasargs && pass == INTERPRET)
|
|
{
|
|
FS_closefiles();
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_CLS(struct Value *value)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET && FS_cls(STDCHANNEL) == -1)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_COLOR(struct Value *value)
|
|
{
|
|
int foreground = -1, background = -1;
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR ||
|
|
(pass != DECLARE && Value_retype(value, V_INTEGER)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
foreground = value->u.integer;
|
|
if (foreground < 0 || foreground > 15)
|
|
{
|
|
Value_destroy(value);
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("foreground colour"));
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR ||
|
|
(pass != DECLARE &&
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
background = value->u.integer;
|
|
if (background < 0 || background > 15)
|
|
{
|
|
Value_destroy(value);
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("background colour"));
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
int bordercolour = -1;
|
|
|
|
if (value->type == V_ERROR ||
|
|
(pass != DECLARE &&
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
bordercolour = value->u.integer;
|
|
if (bordercolour < 0 || bordercolour > 15)
|
|
{
|
|
Value_destroy(value);
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("border colour"));
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
FS_colour(STDCHANNEL, foreground, background);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DATA(struct Value *value)
|
|
{
|
|
if (DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINDIRECTMODE);
|
|
}
|
|
|
|
if (pass == DECLARE)
|
|
{
|
|
*lastdata = pc;
|
|
(lastdata = &(pc.token->u.nextdata))->line = -1;
|
|
}
|
|
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
if (pc.token->type != T_STRING && pc.token->type != T_DATAINPUT)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGDATAINPUT);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DEFFN_DEFPROC_FUNCTION_SUB(struct Value *value)
|
|
{
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
struct Identifier *fn;
|
|
int proc;
|
|
int args = 0;
|
|
|
|
if (DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINDIRECTMODE);
|
|
}
|
|
|
|
proc = (pc.token->type == T_DEFPROC || pc.token->type == T_SUB);
|
|
++pc.token;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
if (proc)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGPROCIDENT);
|
|
}
|
|
else
|
|
{
|
|
return Value_new_ERROR(value, MISSINGFUNCIDENT);
|
|
}
|
|
}
|
|
|
|
fn = pc.token->u.identifier;
|
|
if (proc)
|
|
{
|
|
fn->defaultType = V_VOID;
|
|
}
|
|
|
|
++pc.token;
|
|
if (findLabel(L_FUNC))
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, NESTEDDEFINITION);
|
|
}
|
|
|
|
Auto_variable(&stack, fn);
|
|
if (pc.token->type == T_OP) /* arguments */
|
|
{
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
Auto_funcEnd(&stack);
|
|
return Value_new_ERROR(value, MISSINGFORMIDENT);
|
|
}
|
|
|
|
if (Auto_variable(&stack, pc.token->u.identifier) == 0)
|
|
{
|
|
Auto_funcEnd(&stack);
|
|
return Value_new_ERROR(value, ALREADYDECLARED);
|
|
}
|
|
|
|
++args;
|
|
++pc.token;
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pc.token->type != T_CP)
|
|
{
|
|
Auto_funcEnd(&stack);
|
|
return Value_new_ERROR(value, MISSINGCP);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
|
|
if (pass == DECLARE)
|
|
{
|
|
enum ValueType *t =
|
|
args ? malloc(args * sizeof(enum ValueType)) : (enum ValueType *)0;
|
|
int i;
|
|
|
|
for (i = 0; i < args; ++i)
|
|
{
|
|
t[i] = Auto_argType(&stack, i);
|
|
}
|
|
|
|
if (Global_function
|
|
(&globals, fn, fn->defaultType, &pc, &statementpc, args, t) == 0)
|
|
{
|
|
free(t);
|
|
Auto_funcEnd(&stack);
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
Program_addScope(&program, &fn->sym->u.sub.u.def.scope);
|
|
}
|
|
|
|
pushLabel(L_FUNC, &statementpc);
|
|
if (pc.token->type == T_EQ)
|
|
{
|
|
return stmt_EQ_FNRETURN_FNEND(value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pc = (pc.token + 1)->u.identifier->sym->u.sub.u.def.scope.end;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DEC_INC(struct Value *value)
|
|
{
|
|
int step;
|
|
|
|
step = (pc.token->type == T_DEC ? -1 : 1);
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
struct Value *l, stepValue;
|
|
struct Pc lvaluepc;
|
|
|
|
lvaluepc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGDECINCIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type ==
|
|
T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
if ((l = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (l->type == V_INTEGER)
|
|
{
|
|
VALUE_NEW_INTEGER(&stepValue, step);
|
|
}
|
|
else if (l->type == V_REAL)
|
|
{
|
|
VALUE_NEW_REAL(&stepValue, (double)step);
|
|
}
|
|
else
|
|
{
|
|
pc = lvaluepc;
|
|
return Value_new_ERROR(value, TYPEMISMATCH5);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
Value_add(l, &stepValue, 1);
|
|
}
|
|
|
|
Value_destroy(&stepValue);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DEFINT_DEFDBL_DEFSTR(struct Value *value)
|
|
{
|
|
enum ValueType dsttype = V_NIL;
|
|
|
|
switch (pc.token->type)
|
|
{
|
|
case T_DEFINT:
|
|
dsttype = V_INTEGER;
|
|
break;
|
|
|
|
case T_DEFDBL:
|
|
dsttype = V_REAL;
|
|
break;
|
|
|
|
case T_DEFSTR:
|
|
dsttype = V_STRING;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
struct Identifier *ident;
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGVARIDENT);
|
|
}
|
|
|
|
if (pc.token->u.identifier->defaultType != V_REAL)
|
|
{
|
|
switch (dsttype)
|
|
{
|
|
case V_INTEGER:
|
|
return Value_new_ERROR(value, BADIDENTIFIER, _("integer"));
|
|
|
|
case V_REAL:
|
|
return Value_new_ERROR(value, BADIDENTIFIER, _("real"));
|
|
|
|
case V_STRING:
|
|
return Value_new_ERROR(value, BADIDENTIFIER, _("string"));
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
ident = pc.token->u.identifier;
|
|
++pc.token;
|
|
if (pc.token->type == T_MINUS)
|
|
{
|
|
struct Identifier i;
|
|
|
|
if (strlen(ident->name) != 1)
|
|
{
|
|
return Value_new_ERROR(value, BADRANGE);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGVARIDENT);
|
|
}
|
|
|
|
if (strlen(pc.token->u.identifier->name) != 1)
|
|
{
|
|
return Value_new_ERROR(value, BADRANGE);
|
|
}
|
|
|
|
for (i.name[0] = tolower(ident->name[0]), i.name[1] = '\0';
|
|
i.name[0] <= tolower(pc.token->u.identifier->name[0]);
|
|
++i.name[0])
|
|
{
|
|
Global_variable(&globals, &i, dsttype, GLOBALVAR, 1);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
Global_variable(&globals, ident, dsttype, GLOBALVAR, 1);
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DELETE(struct Value *value)
|
|
{
|
|
struct Pc from, to;
|
|
int f = 0, t = 0;
|
|
|
|
if (pass == INTERPRET && !DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINPROGRAMMODE);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
if (pass == INTERPRET &&
|
|
Program_goLine(&program, pc.token->u.integer,
|
|
&from) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
f = 1;
|
|
++pc.token;
|
|
}
|
|
|
|
if (pc.token->type == T_MINUS || pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
if (pass == INTERPRET &&
|
|
Program_goLine(&program, pc.token->u.integer,
|
|
&to) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
t = 1;
|
|
++pc.token;
|
|
}
|
|
}
|
|
else if (f == 1)
|
|
{
|
|
to = from;
|
|
t = 1;
|
|
}
|
|
|
|
if (!f && !t)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGLINENUMBER);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
Program_delete(&program, f ? &from : (struct Pc *)0,
|
|
t ? &to : (struct Pc *)0);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DIM(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
unsigned int capacity = 0, *geometry = (unsigned int *)0;
|
|
struct Var *var;
|
|
struct Pc dimpc;
|
|
unsigned int dim;
|
|
enum ValueType vartype;
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGARRIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
var = &pc.token->u.identifier->sym->u.var;
|
|
if (pass == INTERPRET && var->dim)
|
|
{
|
|
return Value_new_ERROR(value, REDIM);
|
|
}
|
|
|
|
vartype = var->type;
|
|
++pc.token;
|
|
if (pc.token->type != T_OP)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGOP);
|
|
}
|
|
|
|
++pc.token;
|
|
dim = 0;
|
|
while (1)
|
|
{
|
|
dimpc = pc;
|
|
if (eval(value, _("dimension"))->type == V_ERROR ||
|
|
(pass != DECLARE &&
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR))
|
|
{
|
|
if (capacity)
|
|
{
|
|
free(geometry);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET && value->u.integer < optionbase) /* error
|
|
*/
|
|
{
|
|
Value_destroy(value);
|
|
Value_new_ERROR(value, OUTOFRANGE, _("dimension"));
|
|
}
|
|
|
|
if (value->type == V_ERROR) /* abort */
|
|
{
|
|
if (capacity)
|
|
{
|
|
free(geometry);
|
|
}
|
|
|
|
pc = dimpc;
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (dim == capacity) /* enlarge geometry */
|
|
{
|
|
unsigned int *more;
|
|
|
|
more =
|
|
realloc(geometry,
|
|
sizeof(unsigned int) *
|
|
(capacity ? (capacity *= 2) : (capacity = 3)));
|
|
geometry = more;
|
|
}
|
|
|
|
geometry[dim] = value->u.integer - optionbase + 1;
|
|
++dim;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pc.token->type != T_CP) /* abort */
|
|
{
|
|
if (capacity)
|
|
{
|
|
free(geometry);
|
|
}
|
|
|
|
return Value_new_ERROR(value, MISSINGCP);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
struct Var newarray;
|
|
|
|
assert(capacity);
|
|
if (Var_new(&newarray, vartype, dim, geometry, optionbase) ==
|
|
(struct Var *)0)
|
|
{
|
|
free(geometry);
|
|
return Value_new_ERROR(value, OUTOFMEMORY);
|
|
}
|
|
|
|
Var_destroy(var);
|
|
*var = newarray;
|
|
free(geometry);
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token; /* advance to next var */
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DISPLAY(struct Value *value)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("file name"))->type == V_ERROR ||
|
|
(pass != DECLARE && Value_retype(value, V_STRING)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET && cat(value->u.string.character) == -1)
|
|
{
|
|
const char *msg = strerror(errno);
|
|
|
|
Value_destroy(value);
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, msg);
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DO(struct Value *value)
|
|
{
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
pushLabel(L_DO, &pc);
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_DOcondition(struct Value *value)
|
|
{
|
|
struct Pc dowhilepc = pc;
|
|
int negate = (pc.token->type == T_DOUNTIL);
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
pushLabel(L_DOcondition, &pc);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, "condition")->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
int condition;
|
|
|
|
condition = Value_isNull(value);
|
|
if (negate)
|
|
{
|
|
condition = !condition;
|
|
}
|
|
|
|
if (condition)
|
|
{
|
|
pc = dowhilepc.token->u.exitdo;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_EDIT(struct Value *value)
|
|
{
|
|
#ifdef CONFIG_ARCH_HAVE_VFORK
|
|
long int line;
|
|
struct Pc statementpc = pc;
|
|
int status;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
struct Pc where;
|
|
|
|
if (program.numbered)
|
|
{
|
|
if (Program_goLine(&program, pc.token->u.integer, &where) ==
|
|
(struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
line = where.line + 1;
|
|
}
|
|
else
|
|
{
|
|
if (!Program_end(&program, &where))
|
|
{
|
|
return Value_new_ERROR(value, NOPROGRAM);
|
|
}
|
|
|
|
line = pc.token->u.integer;
|
|
if (line < 1 || line > (where.line + 1))
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
}
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
line = 1;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
/* variables */
|
|
|
|
char *name;
|
|
int chn;
|
|
struct Program newProgram;
|
|
const char *visual, *basename, *shell;
|
|
struct String cmd;
|
|
static struct
|
|
{
|
|
const char *editor, *flag;
|
|
}
|
|
gotoLine[] =
|
|
{
|
|
{
|
|
"Xemacs", "+%ld "},
|
|
{
|
|
"cemacs", "+%ld "},
|
|
{
|
|
"emacs", "+%ld "},
|
|
{
|
|
"emori", "-l%ld "},
|
|
{
|
|
"fe", "-l%ld "},
|
|
{
|
|
"jed", "+%ld "},
|
|
{
|
|
"jmacs", "+%ld "},
|
|
{
|
|
"joe", "+%ld "},
|
|
{
|
|
"modeori", "-l%ld "},
|
|
{
|
|
"origami", "-l%ld "},
|
|
{
|
|
"vi", "-c%ld "},
|
|
{
|
|
"vim", "+%ld "},
|
|
{
|
|
"xemacs", "+%ld "}
|
|
};
|
|
unsigned int i;
|
|
pid_t pid;
|
|
|
|
if (!DIRECTMODE)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, NOTINPROGRAMMODE);
|
|
}
|
|
|
|
if ((name = tmpnam(NULL)) == (char *)0)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR,
|
|
_("generating temporary file name failed"));
|
|
}
|
|
|
|
if ((chn = FS_openout(name)) == -1)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERRORCREATE, name, FS_errmsg);
|
|
}
|
|
|
|
FS_width(chn, 0);
|
|
if (Program_list(&program, chn, 0, (struct Pc *)0, (struct Pc *)0, value))
|
|
{
|
|
pc = statementpc;
|
|
return value;
|
|
}
|
|
|
|
if (FS_close(chn) == -1)
|
|
{
|
|
pc = statementpc;
|
|
unlink(name);
|
|
return Value_new_ERROR(value, IOERRORCLOSE, name, FS_errmsg);
|
|
}
|
|
|
|
if ((visual = getenv("VISUAL")) == (char *)0 &&
|
|
(visual = getenv("EDITOR")) == (char *)0)
|
|
{
|
|
visual = "vi";
|
|
}
|
|
|
|
basename = strrchr(visual, '/');
|
|
if (basename == (char *)0)
|
|
{
|
|
basename = visual;
|
|
}
|
|
|
|
if ((shell = getenv("SHELL")) == (char *)0)
|
|
{
|
|
shell = "/bin/sh";
|
|
}
|
|
|
|
String_new(&cmd);
|
|
String_appendChars(&cmd, visual);
|
|
String_appendChar(&cmd, ' ');
|
|
for (i = 0; i < sizeof(gotoLine) / sizeof(gotoLine[0]); ++i)
|
|
{
|
|
if (strcmp(basename, gotoLine[i].editor) == 0)
|
|
{
|
|
String_appendPrintf(&cmd, gotoLine[i].flag, line);
|
|
break;
|
|
}
|
|
}
|
|
|
|
String_appendChars(&cmd, name);
|
|
FS_shellmode(STDCHANNEL);
|
|
switch (pid = vfork())
|
|
{
|
|
case -1:
|
|
{
|
|
unlink(name);
|
|
FS_fsmode(STDCHANNEL);
|
|
return Value_new_ERROR(value, FORKFAILED, strerror(errno));
|
|
}
|
|
|
|
case 0:
|
|
{
|
|
execl(shell, shell, "-c", cmd.character, (const char *)0);
|
|
exit(127);
|
|
}
|
|
|
|
default:
|
|
{
|
|
/* Wait for the editor to complete */
|
|
|
|
while (waitpid(pid, &status, 0) < 0 && errno != EINTR);
|
|
}
|
|
}
|
|
|
|
FS_fsmode(STDCHANNEL);
|
|
String_destroy(&cmd);
|
|
if ((chn = FS_openin(name)) == -1)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROROPEN, name, FS_errmsg);
|
|
}
|
|
|
|
Program_new(&newProgram);
|
|
if (Program_merge(&newProgram, chn, value))
|
|
{
|
|
FS_close(chn);
|
|
unlink(name);
|
|
pc = statementpc;
|
|
return value;
|
|
}
|
|
|
|
FS_close(chn);
|
|
Program_setname(&newProgram, program.name.character);
|
|
Program_destroy(&program);
|
|
program = newProgram;
|
|
unlink(name);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
#else
|
|
return Value_new_ERROR(value, FORKFAILED, strerror(ENOSYS));
|
|
#endif
|
|
}
|
|
|
|
struct Value *stmt_ELSE_ELSEIFELSE(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
pc = pc.token->u.endifpc;
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Pc elsepc = pc;
|
|
struct Pc *ifinstr;
|
|
int elseifelse = (pc.token->type == T_ELSEIFELSE);
|
|
|
|
if ((ifinstr = popLabel(L_IF)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYELSE1);
|
|
}
|
|
|
|
if (ifinstr->token->type == T_ELSEIFIF)
|
|
{
|
|
(ifinstr->token - 1)->u.elsepc = pc;
|
|
}
|
|
|
|
++pc.token;
|
|
ifinstr->token->u.elsepc = pc;
|
|
assert(ifinstr->token->type == T_ELSEIFIF ||
|
|
ifinstr->token->type == T_IF);
|
|
if (elseifelse)
|
|
{
|
|
return &more_statements;
|
|
}
|
|
else
|
|
{
|
|
pushLabel(L_ELSE, &elsepc);
|
|
}
|
|
}
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_END(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
pc = pc.token->u.endpc;
|
|
bas_end = 1;
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if (Program_end(&program, &pc.token->u.endpc))
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
struct Token *eol;
|
|
|
|
for (eol = pc.token; eol->type != T_EOL; ++eol);
|
|
|
|
pc.token->u.endpc = pc;
|
|
pc.token->u.endpc.token = eol;
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ENDIF(struct Value *value)
|
|
{
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Pc endifpc = pc;
|
|
struct Pc *ifpc;
|
|
struct Pc *elsepc;
|
|
|
|
if ((ifpc = popLabel(L_IF)))
|
|
{
|
|
ifpc->token->u.elsepc = endifpc;
|
|
if (ifpc->token->type == T_ELSEIFIF)
|
|
{
|
|
(ifpc->token - 1)->u.elsepc = pc;
|
|
}
|
|
}
|
|
else if ((elsepc = popLabel(L_ELSE)))
|
|
{
|
|
elsepc->token->u.endifpc = endifpc;
|
|
}
|
|
else
|
|
{
|
|
return Value_new_ERROR(value, STRAYENDIF);
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ENDFN(struct Value *value)
|
|
{
|
|
struct Pc *curfn = (struct Pc *)0;
|
|
struct Pc eqpc = pc;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYENDFN);
|
|
}
|
|
|
|
if ((eqpc.token->u.type =
|
|
(curfn->token + 1)->u.identifier->defaultType) == V_VOID)
|
|
{
|
|
return Value_new_ERROR(value, STRAYENDFN);
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
return Value_clone(value,
|
|
Var_value(Auto_local(&stack, 0), 0, (int *)0,
|
|
(struct Value *)0));
|
|
}
|
|
else
|
|
{
|
|
if (pass == DECLARE)
|
|
{
|
|
Global_endfunction(&globals, (curfn->token + 1)->u.identifier, &pc);
|
|
}
|
|
Auto_funcEnd(&stack);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ENDPROC_SUBEND(struct Value *value)
|
|
{
|
|
struct Pc *curfn = (struct Pc *)0;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0 ||
|
|
(curfn->token + 1)->u.identifier->defaultType != V_VOID)
|
|
{
|
|
if (curfn != (struct Pc *)0)
|
|
{
|
|
pushLabel(L_FUNC, curfn);
|
|
}
|
|
|
|
return Value_new_ERROR(value, STRAYSUBEND, topLabelDescription());
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
return Value_new_VOID(value);
|
|
}
|
|
else
|
|
{
|
|
if (pass == DECLARE)
|
|
{
|
|
Global_endfunction(&globals, (curfn->token + 1)->u.identifier, &pc);
|
|
}
|
|
|
|
Auto_funcEnd(&stack);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ENDSELECT(struct Value *value)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Pc *selectcasepc;
|
|
|
|
if ((selectcasepc = popLabel(L_SELECTCASE)))
|
|
{
|
|
selectcasepc->token->u.selectcase->endselect = pc;
|
|
}
|
|
else
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, STRAYENDSELECT);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ENVIRON(struct Value *value)
|
|
{
|
|
struct Pc epc = pc;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("environment variable"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET && value->u.string.character)
|
|
{
|
|
if (putenv(value->u.string.character) == -1)
|
|
{
|
|
Value_destroy(value);
|
|
pc = epc;
|
|
return Value_new_ERROR(value, ENVIRONFAILED, strerror(errno));
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_FNEXIT(struct Value *value)
|
|
{
|
|
struct Pc *curfn = (struct Pc *)0;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0 ||
|
|
(curfn->token + 1)->u.identifier->defaultType == V_VOID)
|
|
{
|
|
return Value_new_ERROR(value, STRAYFNEXIT);
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
return Value_clone(value,
|
|
Var_value(Auto_local(&stack, 0), 0, (int *)0,
|
|
(struct Value *)0));
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_COLON_EOL(struct Value *value)
|
|
{
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_QUOTE_REM(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_EQ_FNRETURN_FNEND(struct Value *value)
|
|
{
|
|
struct Pc *curfn = (struct Pc *)0;
|
|
struct Pc eqpc = pc;
|
|
enum TokenType t = pc.token->type;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if (t == T_EQ)
|
|
{
|
|
if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYENDEQ);
|
|
}
|
|
|
|
if ((eqpc.token->u.type =
|
|
(curfn->token + 1)->u.identifier->defaultType) == V_VOID)
|
|
{
|
|
return Value_new_ERROR(value, STRAYENDEQ);
|
|
}
|
|
}
|
|
else if (t == T_FNEND)
|
|
{
|
|
if ((curfn = popLabel(L_FUNC)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYENDFN);
|
|
}
|
|
|
|
if ((eqpc.token->u.type =
|
|
(curfn->token + 1)->u.identifier->defaultType) == V_VOID)
|
|
{
|
|
return Value_new_ERROR(value, STRAYENDFN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYFNRETURN);
|
|
}
|
|
|
|
if ((eqpc.token->u.type =
|
|
(curfn->token + 1)->u.identifier->defaultType) == V_VOID)
|
|
{
|
|
return Value_new_ERROR(value, STRAYFNRETURN);
|
|
}
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("return"))->type == V_ERROR ||
|
|
Value_retype(value, eqpc.token->u.type)->type == V_ERROR)
|
|
{
|
|
if (pass != INTERPRET)
|
|
{
|
|
Auto_funcEnd(&stack);
|
|
}
|
|
|
|
pc = eqpc;
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
if (t == T_EQ || t == T_FNEND)
|
|
{
|
|
if (pass == DECLARE)
|
|
{
|
|
Global_endfunction(&globals, (curfn->token + 1)->u.identifier,
|
|
&pc);
|
|
}
|
|
|
|
Auto_funcEnd(&stack);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ERASE(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGARRIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
Var_destroy(&pc.token->u.identifier->sym->u.var);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_EXITDO(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
pc = pc.token->u.exitdo;
|
|
}
|
|
else
|
|
{
|
|
if (pass == COMPILE)
|
|
{
|
|
struct Pc *exitdo;
|
|
|
|
if ((exitdo = findLabel(L_DO)) == (struct Pc *)0 &&
|
|
(exitdo = findLabel(L_DOcondition)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYEXITDO);
|
|
}
|
|
|
|
pc.token->u.exitdo = exitdo->token->u.exitdo;
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_EXITFOR(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
pc = pc.token->u.exitfor;
|
|
}
|
|
else
|
|
{
|
|
if (pass == COMPILE)
|
|
{
|
|
struct Pc *exitfor;
|
|
|
|
if ((exitfor = findLabel(L_FOR)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYEXITFOR);
|
|
}
|
|
|
|
pc.token->u.exitfor = exitfor->token->u.exitfor;
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_FIELD(struct Value *value)
|
|
{
|
|
long int chn, offset, recLength = -1;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && (recLength = FS_recLength(chn)) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
++pc.token;
|
|
offset = 0;
|
|
while (1)
|
|
{
|
|
struct Pc curpc;
|
|
struct Value *l;
|
|
long int width;
|
|
|
|
curpc = pc;
|
|
if (eval(value, _("field width"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
width = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && offset + width > recLength)
|
|
{
|
|
pc = curpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("field width"));
|
|
}
|
|
|
|
if (pc.token->type != T_AS)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGAS);
|
|
}
|
|
|
|
++pc.token;
|
|
curpc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGVARIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type ==
|
|
T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
if ((l = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass != DECLARE && l->type != V_STRING)
|
|
{
|
|
pc = curpc;
|
|
return Value_new_ERROR(value, TYPEMISMATCH4);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
FS_field(chn, &l->u.string, offset, width);
|
|
}
|
|
|
|
offset += width;
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_FOR(struct Value *value)
|
|
{
|
|
struct Pc forpc = pc;
|
|
struct Pc varpc;
|
|
struct Pc limitpc;
|
|
struct Value limit, stepValue;
|
|
|
|
++pc.token;
|
|
varpc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGLOOPIDENT);
|
|
}
|
|
|
|
if (assign(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
++pc.token;
|
|
if (eval(&limit, (const char *)0)->type == V_ERROR)
|
|
{
|
|
*value = limit;
|
|
return value;
|
|
}
|
|
|
|
Value_retype(&limit, value->type);
|
|
assert(limit.type != V_ERROR);
|
|
if (pc.token->type == T_STEP) /* STEP x */
|
|
{
|
|
struct Pc stepPc;
|
|
|
|
++pc.token;
|
|
stepPc = pc;
|
|
if (eval(&stepValue, "`step'")->type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = stepValue;
|
|
pc = stepPc;
|
|
return value;
|
|
}
|
|
|
|
Value_retype(&stepValue, value->type);
|
|
assert(stepValue.type != V_ERROR);
|
|
}
|
|
else /* implicit numeric STEP */
|
|
{
|
|
if (value->type == V_INTEGER)
|
|
{
|
|
VALUE_NEW_INTEGER(&stepValue, 1);
|
|
}
|
|
else
|
|
{
|
|
VALUE_NEW_REAL(&stepValue, 1.0);
|
|
}
|
|
}
|
|
|
|
if (Value_exitFor(value, &limit, &stepValue))
|
|
{
|
|
pc = forpc.token->u.exitfor;
|
|
}
|
|
|
|
Value_destroy(&limit);
|
|
Value_destroy(&stepValue);
|
|
Value_destroy(value);
|
|
}
|
|
else
|
|
{
|
|
pushLabel(L_FOR, &forpc);
|
|
pushLabel(L_FOR_VAR, &varpc);
|
|
if (pc.token->type != T_TO)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGTO);
|
|
}
|
|
|
|
++pc.token;
|
|
pushLabel(L_FOR_LIMIT, &pc);
|
|
limitpc = pc;
|
|
if (eval(&limit, (const char *)0) == (struct Value *)0)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGEXPR, "`to'");
|
|
}
|
|
|
|
if (limit.type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = limit;
|
|
return value;
|
|
}
|
|
|
|
if (pass != DECLARE)
|
|
{
|
|
struct Symbol *sym = varpc.token->u.identifier->sym;
|
|
|
|
if (VALUE_RETYPE
|
|
(&limit, sym->type == GLOBALVAR ||
|
|
sym->type == GLOBALARRAY ? sym->u.var.type : Auto_varType(&stack,
|
|
sym))->type
|
|
== V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = limit;
|
|
pc = limitpc;
|
|
return value;
|
|
}
|
|
}
|
|
|
|
Value_destroy(&limit);
|
|
if (pc.token->type == T_STEP) /* STEP x */
|
|
{
|
|
struct Pc stepPc;
|
|
|
|
++pc.token;
|
|
stepPc = pc;
|
|
if (eval(&stepValue, "`step'")->type == V_ERROR ||
|
|
(pass != DECLARE &&
|
|
Value_retype(&stepValue, value->type)->type == V_ERROR))
|
|
{
|
|
Value_destroy(value);
|
|
*value = stepValue;
|
|
pc = stepPc;
|
|
return value;
|
|
}
|
|
}
|
|
else /* implicit numeric STEP */
|
|
{
|
|
VALUE_NEW_INTEGER(&stepValue, 1);
|
|
if (pass != DECLARE &&
|
|
VALUE_RETYPE(&stepValue, value->type)->type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = stepValue;
|
|
Value_errorPrefix(value, _("implicit STEP 1:"));
|
|
return value;
|
|
}
|
|
}
|
|
|
|
pushLabel(L_FOR_BODY, &pc);
|
|
Value_destroy(&stepValue);
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_GET_PUT(struct Value *value)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
int put = pc.token->type == T_PUT;
|
|
long int chn;
|
|
struct Pc errpc;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
errpc = pc;
|
|
if (eval(value, (const char *)0)) /* process record number/position */
|
|
{
|
|
int rec;
|
|
|
|
if (value->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
rec = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (rec < 1)
|
|
{
|
|
pc = errpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("record number"));
|
|
}
|
|
|
|
if (FS_seek((int)chn, rec - 1) == -1)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA) /* BINARY mode get/put */
|
|
{
|
|
int res = -1;
|
|
|
|
++pc.token;
|
|
if (put)
|
|
{
|
|
if (eval(value, _("`put'/`get' data"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
switch (value->type)
|
|
{
|
|
case V_INTEGER:
|
|
res = FS_putbinaryInteger(chn, value->u.integer);
|
|
break;
|
|
|
|
case V_REAL:
|
|
res = FS_putbinaryReal(chn, value->u.real);
|
|
break;
|
|
|
|
case V_STRING:
|
|
res = FS_putbinaryString(chn, &value->u.string);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
else
|
|
{
|
|
struct Value *l;
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGPROCIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE)
|
|
{
|
|
if (((pc.token + 1)->type == T_OP ||
|
|
Auto_find(&stack, pc.token->u.identifier) == 0) &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type ==
|
|
T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
}
|
|
|
|
if ((l = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
switch (l->type)
|
|
{
|
|
case V_INTEGER:
|
|
res = FS_getbinaryInteger(chn, &l->u.integer);
|
|
break;
|
|
|
|
case V_REAL:
|
|
res = FS_getbinaryReal(chn, &l->u.real);
|
|
break;
|
|
|
|
case V_STRING:
|
|
res = FS_getbinaryString(chn, &l->u.string);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET && res == -1)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
else if (pass == INTERPRET && ((put ? FS_put : FS_get) (chn)) == -1)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_GOSUB(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (!program.runnable &&
|
|
compileProgram(value, !DIRECTMODE)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
pc.token += 2;
|
|
Auto_pushGosubRet(&stack, &pc);
|
|
pc = (pc.token - 2)->u.gosubpc;
|
|
Program_trace(&program, &pc, 0, 1);
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Token *gosubpc = pc.token;
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_INTEGER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGLINENUMBER);
|
|
}
|
|
|
|
if (Program_goLine(&program, pc.token->u.integer, &gosubpc->u.gosubpc) ==
|
|
(struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
if (pass == COMPILE &&
|
|
Program_scopeCheck(&program, &gosubpc->u.gosubpc, findLabel(L_FUNC)))
|
|
{
|
|
return Value_new_ERROR(value, OUTOFSCOPE);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_RESUME_GOTO(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (!program.runnable &&
|
|
compileProgram(value, !DIRECTMODE)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type == T_RESUME)
|
|
{
|
|
if (!stack.resumeable)
|
|
{
|
|
return Value_new_ERROR(value, STRAYRESUME);
|
|
}
|
|
|
|
stack.resumeable = 0;
|
|
}
|
|
|
|
pc = pc.token->u.gotopc;
|
|
Program_trace(&program, &pc, 0, 1);
|
|
}
|
|
else if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Token *gotopc = pc.token;
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_INTEGER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGLINENUMBER);
|
|
}
|
|
|
|
if (Program_goLine(&program, pc.token->u.integer, &gotopc->u.gotopc) ==
|
|
(struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
if (pass == COMPILE &&
|
|
Program_scopeCheck(&program, &gotopc->u.gotopc, findLabel(L_FUNC)))
|
|
{
|
|
return Value_new_ERROR(value, OUTOFSCOPE);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_KILL(struct Value *value)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("file name"))->type == V_ERROR ||
|
|
(pass != DECLARE && Value_retype(value, V_STRING)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET && unlink(value->u.string.character) == -1)
|
|
{
|
|
const char *msg = strerror(errno);
|
|
|
|
Value_destroy(value);
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, msg);
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LET(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGVARIDENT);
|
|
}
|
|
|
|
if (assign(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass != INTERPRET)
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LINEINPUT(struct Value *value)
|
|
{
|
|
int channel = 0;
|
|
struct Pc lpc;
|
|
struct Value *l;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
channel = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
else
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
/* prompt */
|
|
|
|
if (pc.token->type == T_STRING)
|
|
{
|
|
if (pass == INTERPRET && channel == 0)
|
|
{
|
|
FS_putString(channel, pc.token->u.string);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_SEMICOLON && pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGSEMICOMMA);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
|
|
if (pass == INTERPRET && channel == 0)
|
|
{
|
|
FS_flush(channel);
|
|
}
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGVARIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type == T_OP ? GLOBALARRAY : GLOBALVAR,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
lpc = pc;
|
|
if (((l = lvalue(value))->type) == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == COMPILE && l->type != V_STRING)
|
|
{
|
|
pc = lpc;
|
|
return Value_new_ERROR(value, TYPEMISMATCH4);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
String_size(&l->u.string, 0);
|
|
if (FS_appendToString(channel, &l->u.string, 1) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (l->u.string.length == 0)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, _("end of file"));
|
|
}
|
|
|
|
if (l->u.string.character[l->u.string.length - 1] == '\n')
|
|
{
|
|
String_size(&l->u.string, l->u.string.length - 1);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LIST_LLIST(struct Value *value)
|
|
{
|
|
struct Pc from, to;
|
|
int f = 0, t = 0, channel;
|
|
|
|
channel = (pc.token->type == T_LLIST ? LPCHANNEL : STDCHANNEL);
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
if (pass == INTERPRET &&
|
|
Program_fromLine(&program, pc.token->u.integer,
|
|
&from) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
f = 1;
|
|
++pc.token;
|
|
}
|
|
else if (pc.token->type != T_MINUS && pc.token->type != T_COMMA)
|
|
{
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR ||
|
|
(pass != DECLARE &&
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET &&
|
|
Program_fromLine(&program, value->u.integer,
|
|
&from) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
f = 1;
|
|
Value_destroy(value);
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_MINUS || pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR ||
|
|
(pass != DECLARE &&
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET &&
|
|
Program_toLine(&program, value->u.integer, &to) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
t = 1;
|
|
Value_destroy(value);
|
|
}
|
|
}
|
|
else if (f == 1)
|
|
{
|
|
to = from;
|
|
t = 1;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
/* Some implementations do not require direct mode */
|
|
|
|
if (Program_list
|
|
(&program, channel, channel == STDCHANNEL, f ? &from : (struct Pc *)0,
|
|
t ? &to : (struct Pc *)0, value))
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LOAD(struct Value *value)
|
|
{
|
|
struct Pc loadpc;
|
|
|
|
if (pass == INTERPRET && !DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINPROGRAMMODE);
|
|
}
|
|
|
|
++pc.token;
|
|
loadpc = pc;
|
|
if (eval(value, _("file name"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
pc = loadpc;
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
int dev;
|
|
|
|
new();
|
|
Program_setname(&program, value->u.string.character);
|
|
if ((dev = FS_openin(value->u.string.character)) == -1)
|
|
{
|
|
pc = loadpc;
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
FS_width(dev, 0);
|
|
Value_destroy(value);
|
|
if (Program_merge(&program, dev, value))
|
|
{
|
|
pc = loadpc;
|
|
return value;
|
|
}
|
|
|
|
FS_close(dev);
|
|
program.unsaved = 0;
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LOCAL(struct Value *value)
|
|
{
|
|
struct Pc *curfn = (struct Pc *)0;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0)
|
|
return Value_new_ERROR(value, STRAYLOCAL);
|
|
}
|
|
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGVARIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Symbol *fnsym;
|
|
|
|
if (Auto_variable(&stack, pc.token->u.identifier) == 0)
|
|
return Value_new_ERROR(value, ALREADYLOCAL);
|
|
if (pass == DECLARE)
|
|
{
|
|
assert(curfn->token->type == T_DEFFN ||
|
|
curfn->token->type == T_DEFPROC ||
|
|
curfn->token->type == T_SUB ||
|
|
curfn->token->type == T_FUNCTION);
|
|
fnsym = (curfn->token + 1)->u.identifier->sym;
|
|
assert(fnsym);
|
|
fnsym->u.sub.u.def.localTypes =
|
|
realloc(fnsym->u.sub.u.def.localTypes,
|
|
sizeof(enum ValueType) *
|
|
(fnsym->u.sub.u.def.localLength + 1));
|
|
fnsym->u.sub.u.def.localTypes[fnsym->u.sub.u.def.localLength] =
|
|
pc.token->u.identifier->defaultType;
|
|
++fnsym->u.sub.u.def.localLength;
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LOCATE(struct Value *value)
|
|
{
|
|
long int line, column;
|
|
struct Pc argpc;
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
argpc = pc;
|
|
if (eval(value, _("row"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
line = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && line < 1)
|
|
{
|
|
pc = argpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("row"));
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
argpc = pc;
|
|
if (eval(value, _("column"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
column = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && column < 1)
|
|
{
|
|
pc = argpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("column"));
|
|
}
|
|
|
|
if (pass == INTERPRET && FS_locate(STDCHANNEL, line, column) == -1)
|
|
{
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LOCK_UNLOCK(struct Value *value)
|
|
{
|
|
int lock = pc.token->type == T_LOCK;
|
|
int channel;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
channel = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (FS_lock(channel, 0, 0, lock ? FS_LOCK_EXCLUSIVE : FS_LOCK_NONE, 1) ==
|
|
-1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LOOP(struct Value *value)
|
|
{
|
|
struct Pc looppc = pc;
|
|
struct Pc *dopc;
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
pc = looppc.token->u.dopc;
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((dopc = popLabel(L_DO)) == (struct Pc *)0 &&
|
|
(dopc = popLabel(L_DOcondition)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYLOOP);
|
|
}
|
|
|
|
looppc.token->u.dopc = *dopc;
|
|
dopc->token->u.exitdo = pc;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LOOPUNTIL(struct Value *value)
|
|
{
|
|
struct Pc loopuntilpc = pc;
|
|
struct Pc *dopc;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("condition"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (Value_isNull(value))
|
|
pc = loopuntilpc.token->u.dopc;
|
|
Value_destroy(value);
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((dopc = popLabel(L_DO)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYLOOPUNTIL);
|
|
}
|
|
|
|
loopuntilpc.token->u.until = *dopc;
|
|
dopc->token->u.exitdo = pc;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_LSET_RSET(struct Value *value)
|
|
{
|
|
struct Value *l;
|
|
struct Pc tmppc;
|
|
int lset = (pc.token->type == T_LSET);
|
|
|
|
++pc.token;
|
|
if (pass == DECLARE)
|
|
{
|
|
if (((pc.token + 1)->type == T_OP ||
|
|
Auto_find(&stack, pc.token->u.identifier) == 0) &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type ==
|
|
T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
}
|
|
|
|
tmppc = pc;
|
|
if ((l = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == COMPILE && l->type != V_STRING)
|
|
{
|
|
pc = tmppc;
|
|
return Value_new_ERROR(value, TYPEMISMATCH4);
|
|
}
|
|
|
|
if (pc.token->type != T_EQ)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGEQ);
|
|
}
|
|
|
|
++pc.token;
|
|
tmppc = pc;
|
|
if (eval(value, _("rhs"))->type == V_ERROR ||
|
|
(pass != DECLARE && Value_retype(value, l->type)->type == V_ERROR))
|
|
{
|
|
pc = tmppc;
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
(lset ? String_lset : String_rset) (&l->u.string, &value->u.string);
|
|
}
|
|
|
|
Value_destroy(value);
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_IDENTIFIER(struct Value *value)
|
|
{
|
|
struct Pc here = pc;
|
|
|
|
if (pass == DECLARE)
|
|
{
|
|
if (func(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
if (pc.token->type == T_EQ || pc.token->type == T_COMMA)
|
|
{
|
|
pc = here;
|
|
if (assign(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pass == COMPILE)
|
|
{
|
|
if (((pc.token + 1)->type == T_OP ||
|
|
Auto_find(&stack, pc.token->u.identifier) == 0) &&
|
|
Global_find(&globals, pc.token->u.identifier,
|
|
(pc.token + 1)->type == T_OP) == 0)
|
|
{
|
|
return Value_new_ERROR(value, UNDECLARED);
|
|
}
|
|
}
|
|
|
|
if (strcasecmp(pc.token->u.identifier->name, "mid$")
|
|
&& (pc.token->u.identifier->sym->type == USERFUNCTION ||
|
|
pc.token->u.identifier->sym->type == BUILTINFUNCTION))
|
|
{
|
|
func(value);
|
|
if (Value_retype(value, V_VOID)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
else
|
|
{
|
|
if (assign(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass != INTERPRET)
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_IF_ELSEIFIF(struct Value *value)
|
|
{
|
|
struct Pc ifpc = pc;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("condition"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type != T_THEN)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGTHEN);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (Value_isNull(value))
|
|
{
|
|
pc = ifpc.token->u.elsepc;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_EOL)
|
|
{
|
|
pushLabel(L_IF, &ifpc);
|
|
}
|
|
else /* compile single line IF THEN ELSE recursively
|
|
*/
|
|
{
|
|
if (statements(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_ELSE)
|
|
{
|
|
struct Pc elsepc = pc;
|
|
|
|
++pc.token;
|
|
ifpc.token->u.elsepc = pc;
|
|
if (ifpc.token->type == T_ELSEIFIF)
|
|
{
|
|
(ifpc.token - 1)->u.elsepc = pc;
|
|
}
|
|
|
|
if (statements(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
elsepc.token->u.endifpc = pc;
|
|
}
|
|
else
|
|
{
|
|
ifpc.token->u.elsepc = pc;
|
|
if (ifpc.token->type == T_ELSEIFIF)
|
|
{
|
|
(ifpc.token - 1)->u.elsepc = pc;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_IMAGE(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (pc.token->type != T_STRING)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGFMT);
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_INPUT(struct Value *value)
|
|
{
|
|
int channel = STDCHANNEL;
|
|
int nl = 1;
|
|
int extraprompt = 1;
|
|
struct Token *inputdata = (struct Token *)0, *t = (struct Token *)0;
|
|
struct Pc lvaluepc;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
channel = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
else
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_SEMICOLON)
|
|
{
|
|
nl = 0;
|
|
++pc.token;
|
|
}
|
|
|
|
/* prompt */
|
|
|
|
if (pc.token->type == T_STRING)
|
|
{
|
|
if (pass == INTERPRET && channel == STDCHANNEL)
|
|
{
|
|
FS_putString(STDCHANNEL, pc.token->u.string);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_COMMA || pc.token->type == T_COLON)
|
|
{
|
|
++pc.token;
|
|
extraprompt = 0;
|
|
}
|
|
else if (pc.token->type == T_SEMICOLON)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
extraprompt = 0;
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET && channel == STDCHANNEL && extraprompt)
|
|
{
|
|
FS_putChars(STDCHANNEL, "? ");
|
|
}
|
|
|
|
retry:
|
|
if (pass == INTERPRET) /* read input line and tokenise it */
|
|
{
|
|
struct String s;
|
|
|
|
if (channel == STDCHANNEL)
|
|
{
|
|
FS_flush(STDCHANNEL);
|
|
}
|
|
|
|
String_new(&s);
|
|
if (FS_appendToString(channel, &s, nl) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (s.length == 0)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, _("end of file"));
|
|
}
|
|
|
|
inputdata = t = Token_newData(s.character);
|
|
String_destroy(&s);
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
struct Value *l;
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGVARIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type ==
|
|
T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
lvaluepc = pc;
|
|
if (((l = lvalue(value))->type) == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (t->type == T_COMMA || t->type == T_EOL)
|
|
{
|
|
enum ValueType ltype = l->type;
|
|
|
|
Value_destroy(l);
|
|
Value_new_null(l, ltype);
|
|
}
|
|
else if (convert(value, l, t))
|
|
{
|
|
pc = lvaluepc;
|
|
if (channel == STDCHANNEL)
|
|
{
|
|
struct String s;
|
|
|
|
String_new(&s);
|
|
Value_toString(value, &s, ' ', -1, 0, 0, 0, 0, -1, 0, 0);
|
|
String_appendChars(&s, " ?? ");
|
|
FS_putString(STDCHANNEL, &s);
|
|
String_destroy(&s);
|
|
Value_destroy(value);
|
|
Token_destroy(inputdata);
|
|
goto retry;
|
|
}
|
|
else
|
|
{
|
|
Token_destroy(inputdata);
|
|
return value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++t;
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
if (t->type == T_COMMA)
|
|
{
|
|
++t;
|
|
}
|
|
else
|
|
{
|
|
Token_destroy(inputdata);
|
|
if (channel == STDCHANNEL)
|
|
{
|
|
FS_putChars(STDCHANNEL, "?? ");
|
|
++pc.token;
|
|
goto retry;
|
|
}
|
|
else
|
|
{
|
|
pc = lvaluepc;
|
|
return Value_new_ERROR(value, MISSINGINPUTDATA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (t->type != T_EOL)
|
|
{
|
|
FS_putChars(STDCHANNEL, _("Too much input data\n"));
|
|
}
|
|
|
|
Token_destroy(inputdata);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_MAT(struct Value *value)
|
|
{
|
|
struct Var *var1, *var2, *var3 = (struct Var *)0;
|
|
struct Pc oppc;
|
|
enum TokenType op = T_EOL;
|
|
|
|
oppc.line = -1;
|
|
oppc.token = (struct Token *)0;
|
|
++pc.token;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGMATIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
var1 = &pc.token->u.identifier->sym->u.var;
|
|
++pc.token;
|
|
if (pc.token->type != T_EQ)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGEQ);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_IDENTIFIER) /* a = b [ +|-|* c ] */
|
|
{
|
|
if (pass == COMPILE)
|
|
{
|
|
if (((pc.token + 1)->type == T_OP ||
|
|
Auto_find(&stack, pc.token->u.identifier) == 0) &&
|
|
Global_find(&globals, pc.token->u.identifier, 1) == 0)
|
|
return Value_new_ERROR(value, UNDECLARED);
|
|
}
|
|
|
|
var2 = &pc.token->u.identifier->sym->u.var;
|
|
if (pass == INTERPRET &&
|
|
((var2->dim != 1 && var2->dim != 2) || var2->base < 0 ||
|
|
var2->base > 1))
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var2->dim, var2->base);
|
|
}
|
|
|
|
if (pass == COMPILE &&
|
|
Value_commonType[var1->type][var2->type] == V_ERROR)
|
|
{
|
|
return Value_new_typeError(value, var2->type, var1->type);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_PLUS || pc.token->type == T_MINUS ||
|
|
pc.token->type == T_MULT)
|
|
{
|
|
oppc = pc;
|
|
op = pc.token->type;
|
|
++pc.token;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGARRIDENT);
|
|
}
|
|
|
|
if (pass == COMPILE)
|
|
{
|
|
if (((pc.token + 1)->type == T_OP ||
|
|
Auto_find(&stack, pc.token->u.identifier) == 0) &&
|
|
Global_find(&globals, pc.token->u.identifier, 1) == 0)
|
|
{
|
|
return Value_new_ERROR(value, UNDECLARED);
|
|
}
|
|
}
|
|
|
|
var3 = &pc.token->u.identifier->sym->u.var;
|
|
if (pass == INTERPRET &&
|
|
((var3->dim != 1 && var3->dim != 2) || var3->base < 0 ||
|
|
var3->base > 1))
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var3->dim, var3->base);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
|
|
if (pass != DECLARE)
|
|
{
|
|
if (var3 == (struct Var *)0)
|
|
{
|
|
if (Var_mat_assign(var1, var2, value, pass == INTERPRET))
|
|
{
|
|
assert(oppc.line != -1);
|
|
pc = oppc;
|
|
return value;
|
|
}
|
|
}
|
|
else if (op == T_MULT)
|
|
{
|
|
if (Var_mat_mult(var1, var2, var3, value, pass == INTERPRET))
|
|
{
|
|
assert(oppc.line != -1);
|
|
pc = oppc;
|
|
return value;
|
|
}
|
|
}
|
|
else if (Var_mat_addsub
|
|
(var1, var2, var3, op == T_PLUS, value, pass == INTERPRET))
|
|
{
|
|
assert(oppc.line != -1);
|
|
pc = oppc;
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
else if (pc.token->type == T_OP)
|
|
{
|
|
if (var1->type == V_STRING)
|
|
{
|
|
return Value_new_ERROR(value, TYPEMISMATCH5);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("factor"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == COMPILE &&
|
|
Value_commonType[var1->type][value->type] == V_ERROR)
|
|
{
|
|
return Value_new_typeError(value, var1->type, value->type);
|
|
}
|
|
|
|
if (pc.token->type != T_CP)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGCP);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_MULT)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGMULT);
|
|
}
|
|
|
|
oppc = pc;
|
|
++pc.token;
|
|
if (pass == COMPILE)
|
|
{
|
|
if (((pc.token + 1)->type == T_OP ||
|
|
Auto_find(&stack, pc.token->u.identifier) == 0) &&
|
|
Global_find(&globals, pc.token->u.identifier, 1) == 0)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, UNDECLARED);
|
|
}
|
|
}
|
|
|
|
var2 = &pc.token->u.identifier->sym->u.var;
|
|
if (pass == INTERPRET &&
|
|
((var2->dim != 1 && var2->dim != 2) || var2->base < 0 ||
|
|
var2->base > 1))
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, NOMATRIX, var2->dim, var2->base);
|
|
}
|
|
|
|
if (pass != DECLARE &&
|
|
Var_mat_scalarMult(var1, value, var2, pass == INTERPRET))
|
|
{
|
|
assert(oppc.line != -1);
|
|
pc = oppc;
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
++pc.token;
|
|
}
|
|
|
|
else if (pc.token->type == T_CON || pc.token->type == T_ZER ||
|
|
pc.token->type == T_IDN)
|
|
{
|
|
op = pc.token->type;
|
|
if (pass == COMPILE && Value_commonType[var1->type][V_INTEGER] == V_ERROR)
|
|
{
|
|
return Value_new_typeError(value, V_INTEGER, var1->type);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_OP)
|
|
{
|
|
unsigned int dim, geometry[2];
|
|
enum ValueType vartype = var1->type;
|
|
|
|
++pc.token;
|
|
if (evalGeometry(value, &dim, geometry))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
Var_destroy(var1);
|
|
Var_new(var1, vartype, dim, geometry, optionbase);
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
unsigned int i;
|
|
int unused = 1 - var1->base;
|
|
|
|
if ((var1->dim != 1 && var1->dim != 2) || var1->base < 0 ||
|
|
var1->base > 1)
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var1->dim, var1->base);
|
|
}
|
|
|
|
if (var1->dim == 1)
|
|
{
|
|
for (i = unused; i < var1->geometry[0]; ++i)
|
|
{
|
|
int c = -1;
|
|
|
|
Value_destroy(&(var1->value[i]));
|
|
switch (op)
|
|
{
|
|
case T_CON:
|
|
c = 1;
|
|
break;
|
|
|
|
case T_ZER:
|
|
c = 0;
|
|
break;
|
|
|
|
case T_IDN:
|
|
c = (i == unused ? 1 : 0);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
if (var1->type == V_INTEGER)
|
|
{
|
|
Value_new_INTEGER(&(var1->value[i]), c);
|
|
}
|
|
else
|
|
{
|
|
Value_new_REAL(&(var1->value[i]), (double)c);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int j;
|
|
|
|
for (i = unused; i < var1->geometry[0]; ++i)
|
|
{
|
|
for (j = unused; j < var1->geometry[1]; ++j)
|
|
{
|
|
int c = -1;
|
|
|
|
Value_destroy(&(var1->value[i * var1->geometry[1] + j]));
|
|
switch (op)
|
|
{
|
|
case T_CON:
|
|
c = 1;
|
|
break;
|
|
|
|
case T_ZER:
|
|
c = 0;
|
|
break;
|
|
|
|
case T_IDN:
|
|
c = (i == j ? 1 : 0);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
if (var1->type == V_INTEGER)
|
|
{
|
|
Value_new_INTEGER(&
|
|
(var1->value
|
|
[i * var1->geometry[1] + j]), c);
|
|
}
|
|
else
|
|
{
|
|
Value_new_REAL(&
|
|
(var1->
|
|
value[i * var1->geometry[1] + j]),
|
|
(double)c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (pc.token->type == T_TRN || pc.token->type == T_INV)
|
|
{
|
|
op = pc.token->type;
|
|
++pc.token;
|
|
if (pc.token->type != T_OP)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGOP);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGMATIDENT);
|
|
}
|
|
|
|
if (pass == COMPILE)
|
|
{
|
|
if (((pc.token + 1)->type == T_OP ||
|
|
Auto_find(&stack, pc.token->u.identifier) == 0) &&
|
|
Global_find(&globals, pc.token->u.identifier, 1) == 0)
|
|
{
|
|
return Value_new_ERROR(value, UNDECLARED);
|
|
}
|
|
}
|
|
|
|
var2 = &pc.token->u.identifier->sym->u.var;
|
|
if (pass == COMPILE &&
|
|
Value_commonType[var1->type][var2->type] == V_ERROR)
|
|
{
|
|
return Value_new_typeError(value, var2->type, var1->type);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (var2->dim != 2 || var2->base < 0 || var2->base > 1)
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var2->dim, var2->base);
|
|
}
|
|
|
|
switch (op)
|
|
{
|
|
case T_TRN:
|
|
Var_mat_transpose(var1, var2);
|
|
break;
|
|
|
|
case T_INV:
|
|
if (Var_mat_invert(var1, var2, &stack.lastdet, value))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_CP)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCP);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
return Value_new_ERROR(value, MISSINGEXPR, _("matrix"));
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_MATINPUT(struct Value *value)
|
|
{
|
|
int channel = STDCHANNEL;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
channel = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
else
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
struct Pc lvaluepc;
|
|
struct Var *var;
|
|
|
|
lvaluepc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGMATIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
var = &pc.token->u.identifier->sym->u.var;
|
|
++pc.token;
|
|
if (pc.token->type == T_OP)
|
|
{
|
|
unsigned int dim, geometry[2];
|
|
enum ValueType vartype = var->type;
|
|
|
|
++pc.token;
|
|
if (evalGeometry(value, &dim, geometry))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
Var_destroy(var);
|
|
Var_new(var, vartype, dim, geometry, optionbase);
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
unsigned int i, j;
|
|
int unused = 1 - var->base;
|
|
int columns;
|
|
struct Token *inputdata, *t;
|
|
|
|
if (var->dim != 1 && var->dim != 2)
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var->dim);
|
|
}
|
|
|
|
columns = var->dim == 1 ? 0 : var->geometry[1];
|
|
inputdata = t = (struct Token *)0;
|
|
for (i = unused, j = unused; i < var->geometry[0];)
|
|
{
|
|
struct String s;
|
|
|
|
if (!inputdata)
|
|
{
|
|
if (channel == STDCHANNEL)
|
|
{
|
|
FS_putChars(STDCHANNEL, "? ");
|
|
FS_flush(STDCHANNEL);
|
|
}
|
|
|
|
String_new(&s);
|
|
if (FS_appendToString(channel, &s, 1) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (s.length == 0)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, _("end of file"));
|
|
}
|
|
|
|
inputdata = t = Token_newData(s.character);
|
|
String_destroy(&s);
|
|
}
|
|
|
|
if (t->type == T_COMMA)
|
|
{
|
|
Value_destroy(&(var->value[j * columns + i]));
|
|
Value_new_null(&(var->value[j * columns + i]), var->type);
|
|
++t;
|
|
}
|
|
else if (t->type == T_EOL)
|
|
{
|
|
while (i < var->geometry[0])
|
|
{
|
|
Value_destroy(&(var->value[j * columns + i]));
|
|
Value_new_null(&(var->value[j * columns + i]), var->type);
|
|
++i;
|
|
}
|
|
}
|
|
else if (convert(value, &(var->value[j * columns + i]), t))
|
|
{
|
|
Token_destroy(inputdata);
|
|
pc = lvaluepc;
|
|
return value;
|
|
}
|
|
else
|
|
{
|
|
++t;
|
|
++i;
|
|
if (t->type == T_COMMA)
|
|
{
|
|
++t;
|
|
}
|
|
}
|
|
|
|
if (i == var->geometry[0] && j < (columns - 1))
|
|
{
|
|
i = unused;
|
|
++j;
|
|
if (t->type == T_EOL)
|
|
{
|
|
Token_destroy(inputdata);
|
|
inputdata = (struct Token *)0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_MATPRINT(struct Value *value)
|
|
{
|
|
int chn = STDCHANNEL;
|
|
int printusing = 0;
|
|
struct Value usingval;
|
|
struct String *using = (struct String *)0;
|
|
size_t usingpos = 0;
|
|
int notfirst = 0;
|
|
|
|
++pc.token;
|
|
if (chn == STDCHANNEL && pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_USING)
|
|
{
|
|
struct Pc usingpc;
|
|
|
|
usingpc = pc;
|
|
printusing = 1;
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
if (pass == COMPILE &&
|
|
Program_imageLine(&program, pc.token->u.integer,
|
|
&usingpc.token->u.image) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHIMAGELINE);
|
|
}
|
|
else if (pass == INTERPRET)
|
|
{
|
|
using = usingpc.token->u.image.token->u.string;
|
|
}
|
|
|
|
Value_new_STRING(&usingval);
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
if (eval(&usingval, _("format string"))->type == V_ERROR ||
|
|
Value_retype(&usingval, V_STRING)->type == V_ERROR)
|
|
{
|
|
*value = usingval;
|
|
return value;
|
|
}
|
|
|
|
using = &usingval.u.string;
|
|
}
|
|
|
|
if (pc.token->type != T_SEMICOLON)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, MISSINGSEMICOLON);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
Value_new_STRING(&usingval);
|
|
using = &usingval.u.string;
|
|
}
|
|
while (1)
|
|
{
|
|
struct Var *var;
|
|
int zoned = 1;
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
if (notfirst)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, MISSINGMATIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY,
|
|
0) == 0)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
var = &pc.token->u.identifier->sym->u.var;
|
|
++pc.token;
|
|
if (pc.token->type == T_SEMICOLON)
|
|
{
|
|
zoned = 0;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
unsigned int i, j;
|
|
int unused = 1 - var->base;
|
|
int g0, g1;
|
|
|
|
if ((var->dim != 1 && var->dim != 2) || var->base < 0 ||
|
|
var->base > 1)
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var->dim, var->base);
|
|
}
|
|
|
|
if ((notfirst ? FS_putChar(chn, '\n') : FS_nextline(chn)) == -1)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
g0 = var->geometry[0];
|
|
g1 = var->dim == 1 ? unused + 1 : var->geometry[1];
|
|
for (i = unused; i < g0; ++i)
|
|
{
|
|
for (j = unused; j < g1; ++j)
|
|
{
|
|
struct String s;
|
|
|
|
String_new(&s);
|
|
Value_clone(value,
|
|
&(var->value[var->dim == 1 ? i : i * g1 + j]));
|
|
if (Value_toStringUsing(value, &s, using, &usingpos)->type ==
|
|
V_ERROR)
|
|
{
|
|
Value_destroy(&usingval);
|
|
String_destroy(&s);
|
|
return value;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (FS_putString(chn, &s) == -1)
|
|
{
|
|
Value_destroy(&usingval);
|
|
String_destroy(&s);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
String_destroy(&s);
|
|
if (!printusing && zoned)
|
|
{
|
|
FS_nextcol(chn);
|
|
}
|
|
}
|
|
|
|
if (FS_putChar(chn, '\n') == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA || pc.token->type == T_SEMICOLON)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
notfirst = 1;
|
|
}
|
|
|
|
Value_destroy(&usingval);
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (FS_flush(chn) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_MATREAD(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
struct Pc lvaluepc;
|
|
struct Var *var;
|
|
|
|
lvaluepc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGMATIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
var = &pc.token->u.identifier->sym->u.var;
|
|
++pc.token;
|
|
if (pc.token->type == T_OP)
|
|
{
|
|
unsigned int dim, geometry[2];
|
|
enum ValueType vartype = var->type;
|
|
|
|
++pc.token;
|
|
if (evalGeometry(value, &dim, geometry))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
Var_destroy(var);
|
|
Var_new(var, vartype, dim, geometry, optionbase);
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
unsigned int i;
|
|
int unused = 1 - var->base;
|
|
|
|
if ((var->dim != 1 && var->dim != 2) || var->base < 0 ||
|
|
var->base > 1)
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var->dim, var->base);
|
|
}
|
|
|
|
if (var->dim == 1)
|
|
{
|
|
for (i = unused; i < var->geometry[0]; ++i)
|
|
{
|
|
if (dataread(value, &(var->value[i])))
|
|
{
|
|
pc = lvaluepc;
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int j;
|
|
|
|
for (i = unused; i < var->geometry[0]; ++i)
|
|
{
|
|
for (j = unused; j < var->geometry[1]; ++j)
|
|
{
|
|
if (dataread
|
|
(value, &(var->value[i * var->geometry[1] + j])))
|
|
{
|
|
pc = lvaluepc;
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_MATREDIM(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
struct Var *var;
|
|
unsigned int dim, geometry[2];
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGMATIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
var = &pc.token->u.identifier->sym->u.var;
|
|
++pc.token;
|
|
if (pc.token->type != T_OP)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGOP);
|
|
}
|
|
|
|
++pc.token;
|
|
if (evalGeometry(value, &dim, geometry))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET &&
|
|
Var_mat_redim(var, dim, geometry, value) != (struct Value *)0)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_MATWRITE(struct Value *value)
|
|
{
|
|
int chn = STDCHANNEL;
|
|
int notfirst = 0;
|
|
int comma = 0;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
struct Var *var;
|
|
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
if (notfirst)
|
|
{
|
|
break;
|
|
}
|
|
|
|
return Value_new_ERROR(value, MISSINGMATIDENT);
|
|
}
|
|
|
|
notfirst = 1;
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType, GLOBALARRAY,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
var = &pc.token->u.identifier->sym->u.var;
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
unsigned int i, j;
|
|
int unused = 1 - var->base;
|
|
int g0, g1;
|
|
|
|
if ((var->dim != 1 && var->dim != 2) || var->base < 0 ||
|
|
var->base > 1)
|
|
{
|
|
return Value_new_ERROR(value, NOMATRIX, var->dim, var->base);
|
|
}
|
|
|
|
g0 = var->geometry[0];
|
|
g1 = var->dim == 1 ? unused + 1 : var->geometry[1];
|
|
for (i = unused; i < g0; ++i)
|
|
{
|
|
comma = 0;
|
|
for (j = unused; j < g1; ++j)
|
|
{
|
|
struct String s;
|
|
|
|
String_new(&s);
|
|
Value_clone(value,
|
|
&(var->value[var->dim == 1 ? i : i * g1 + j]));
|
|
if (comma)
|
|
{
|
|
String_appendChar(&s, ',');
|
|
}
|
|
|
|
if (FS_putString(chn, Value_toWrite(value, &s)) == -1)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (FS_flush(chn) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
String_destroy(&s);
|
|
comma = 1;
|
|
}
|
|
|
|
FS_putChar(chn, '\n');
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA || pc.token->type == T_SEMICOLON)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (FS_flush(chn) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_NAME(struct Value *value)
|
|
{
|
|
struct Pc namepc = pc;
|
|
struct Value old;
|
|
int res = -1, reserrno = -1;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("file name"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type != T_AS)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGAS);
|
|
}
|
|
|
|
old = *value;
|
|
++pc.token;
|
|
if (eval(value, _("file name"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
Value_destroy(&old);
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
res = rename(old.u.string.character, value->u.string.character);
|
|
reserrno = errno;
|
|
}
|
|
|
|
Value_destroy(&old);
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && res == -1)
|
|
{
|
|
pc = namepc;
|
|
return Value_new_ERROR(value, IOERROR, strerror(reserrno));
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_NEW(struct Value *value)
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (!DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINPROGRAMMODE);
|
|
}
|
|
|
|
new();
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_NEXT(struct Value *value)
|
|
{
|
|
struct Next **next = &pc.token->u.next;
|
|
int level = 0;
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
struct Value *l, inc;
|
|
struct Pc savepc;
|
|
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
/* get variable lvalue */
|
|
|
|
savepc = pc;
|
|
pc = (*next)[level].var;
|
|
if ((l = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
pc = savepc;
|
|
|
|
/* get limit value and increment */
|
|
|
|
savepc = pc;
|
|
pc = (*next)[level].limit;
|
|
if (eval(value, _("limit"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
Value_retype(value, l->type);
|
|
assert(value->type != V_ERROR);
|
|
if (pc.token->type == T_STEP)
|
|
{
|
|
++pc.token;
|
|
if (eval(&inc, _("step"))->type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = inc;
|
|
return value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VALUE_NEW_INTEGER(&inc, 1);
|
|
}
|
|
|
|
VALUE_RETYPE(&inc, l->type);
|
|
assert(inc.type != V_ERROR);
|
|
pc = savepc;
|
|
|
|
Value_add(l, &inc, 1);
|
|
if (Value_exitFor(l, value, &inc))
|
|
{
|
|
Value_destroy(value);
|
|
Value_destroy(&inc);
|
|
if (pc.token->type == T_IDENTIFIER)
|
|
{
|
|
if (lvalue(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
++level;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pc = (*next)[level].body;
|
|
Value_destroy(value);
|
|
Value_destroy(&inc);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct Pc *body;
|
|
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
if ((body = popLabel(L_FOR_BODY)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYNEXT, topLabelDescription());
|
|
}
|
|
|
|
if (level)
|
|
{
|
|
struct Next *more;
|
|
|
|
more = realloc(*next, sizeof(struct Next) * (level + 1));
|
|
*next = more;
|
|
}
|
|
|
|
(*next)[level].body = *body;
|
|
(*next)[level].limit = *popLabel(L_FOR_LIMIT);
|
|
(*next)[level].var = *popLabel(L_FOR_VAR);
|
|
(*next)[level].fr = *popLabel(L_FOR);
|
|
if (pc.token->type == T_IDENTIFIER)
|
|
{
|
|
if (cistrcmp
|
|
(pc.token->u.identifier->name,
|
|
(*next)[level].var.token->u.identifier->name))
|
|
{
|
|
return Value_new_ERROR(value, FORMISMATCH);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type ==
|
|
T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
if (lvalue(value)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
++level;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (level >= 0)
|
|
{
|
|
(*next)[level--].fr.token->u.exitfor = pc;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ON(struct Value *value)
|
|
{
|
|
struct On *on = &pc.token->u.on;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("selector"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
struct Pc newpc;
|
|
|
|
if (value->u.integer > 0 && value->u.integer < on->pcLength)
|
|
{
|
|
newpc = on->pc[value->u.integer];
|
|
}
|
|
else
|
|
{
|
|
newpc = on->pc[0];
|
|
}
|
|
|
|
if (pc.token->type == T_GOTO)
|
|
{
|
|
pc = newpc;
|
|
}
|
|
else
|
|
{
|
|
pc = on->pc[0];
|
|
Auto_pushGosubRet(&stack, &pc);
|
|
pc = newpc;
|
|
}
|
|
|
|
Program_trace(&program, &pc, 0, 1);
|
|
}
|
|
else if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
Value_destroy(value);
|
|
if (pc.token->type != T_GOTO && pc.token->type != T_GOSUB)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGGOTOSUB);
|
|
}
|
|
|
|
++pc.token;
|
|
on->pcLength = 1;
|
|
while (1)
|
|
{
|
|
on->pc = realloc(on->pc, sizeof(struct Pc) * ++on->pcLength);
|
|
if (pc.token->type != T_INTEGER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGLINENUMBER);
|
|
}
|
|
|
|
if (Program_goLine
|
|
(&program, pc.token->u.integer,
|
|
&on->pc[on->pcLength - 1]) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
if (pass == COMPILE &&
|
|
Program_scopeCheck(&program, &on->pc[on->pcLength - 1],
|
|
findLabel(L_FUNC)))
|
|
{
|
|
return Value_new_ERROR(value, OUTOFSCOPE);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
on->pc[0] = pc;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ONERROR(struct Value *value)
|
|
{
|
|
if (DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINDIRECTMODE);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
stack.onerror = pc;
|
|
Program_nextLine(&program, &pc);
|
|
return (struct Value *)0;
|
|
}
|
|
else
|
|
{
|
|
return &more_statements;
|
|
}
|
|
}
|
|
|
|
struct Value *stmt_ONERRORGOTO0(struct Value *value)
|
|
{
|
|
if (DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINDIRECTMODE);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
stack.onerror.line = -1;
|
|
if (stack.resumeable)
|
|
{
|
|
pc = stack.erpc;
|
|
return Value_clone(value, &stack.err);
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ONERROROFF(struct Value *value)
|
|
{
|
|
if (DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINDIRECTMODE);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
stack.onerror.line = -1;
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_OPEN(struct Value *value)
|
|
{
|
|
int inout = -1, append = 0;
|
|
int mode = FS_ACCESS_NONE, lock = FS_LOCK_NONE;
|
|
long int channel;
|
|
long int recLength = -1;
|
|
struct Pc errpc;
|
|
struct Value recLengthValue;
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
errpc = pc;
|
|
if (eval(value, _("mode or file"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA) /* parse MBASIC syntax */
|
|
{
|
|
if (value->u.string.length >= 1)
|
|
{
|
|
switch (tolower(value->u.string.character[0]))
|
|
{
|
|
case 'i':
|
|
inout = 0;
|
|
mode = FS_ACCESS_READ;
|
|
break;
|
|
|
|
case 'o':
|
|
inout = 1;
|
|
mode = FS_ACCESS_WRITE;
|
|
break;
|
|
|
|
case 'a':
|
|
inout = 1;
|
|
mode = FS_ACCESS_WRITE;
|
|
append = 1;
|
|
break;
|
|
|
|
case 'r':
|
|
inout = 3;
|
|
mode = FS_ACCESS_READWRITE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && inout == -1)
|
|
{
|
|
pc = errpc;
|
|
return Value_new_ERROR(value, BADMODE);
|
|
}
|
|
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
errpc = pc;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
pc = errpc;
|
|
return value;
|
|
}
|
|
|
|
channel = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && channel < 0)
|
|
{
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("channel"));
|
|
}
|
|
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("file name"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (inout == 3)
|
|
{
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
++pc.token;
|
|
errpc = pc;
|
|
if (eval(&recLengthValue, _("record length"))->type == V_ERROR ||
|
|
Value_retype(&recLengthValue, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = recLengthValue;
|
|
return value;
|
|
}
|
|
|
|
recLength = recLengthValue.u.integer;
|
|
Value_destroy(&recLengthValue);
|
|
if (pass == INTERPRET && recLength <= 0)
|
|
{
|
|
Value_destroy(value);
|
|
pc = errpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("record length"));
|
|
}
|
|
}
|
|
}
|
|
else /* parse ANSI syntax */
|
|
{
|
|
struct Value channelValue;
|
|
int newMode;
|
|
|
|
switch (pc.token->type)
|
|
{
|
|
case T_FOR_INPUT:
|
|
inout = 0;
|
|
mode = FS_ACCESS_READ;
|
|
++pc.token;
|
|
break;
|
|
|
|
case T_FOR_OUTPUT:
|
|
inout = 1;
|
|
mode = FS_ACCESS_WRITE;
|
|
++pc.token;
|
|
break;
|
|
|
|
case T_FOR_APPEND:
|
|
inout = 1;
|
|
mode = FS_ACCESS_WRITE;
|
|
append = 1;
|
|
++pc.token;
|
|
break;
|
|
|
|
case T_FOR_RANDOM:
|
|
inout = 3;
|
|
mode = FS_ACCESS_READWRITE;
|
|
++pc.token;
|
|
break;
|
|
|
|
case T_FOR_BINARY:
|
|
inout = 4;
|
|
mode = FS_ACCESS_READWRITE;
|
|
++pc.token;
|
|
break;
|
|
|
|
default:
|
|
inout = 3;
|
|
mode = FS_ACCESS_READWRITE;
|
|
break;
|
|
}
|
|
|
|
switch (pc.token->type)
|
|
{
|
|
case T_ACCESS_READ:
|
|
newMode = FS_ACCESS_READ;
|
|
break;
|
|
|
|
case T_ACCESS_READ_WRITE:
|
|
newMode = FS_ACCESS_READWRITE;
|
|
break;
|
|
|
|
case T_ACCESS_WRITE:
|
|
newMode = FS_ACCESS_WRITE;
|
|
break;
|
|
|
|
default:
|
|
newMode = FS_ACCESS_NONE;
|
|
}
|
|
|
|
if (newMode != FS_ACCESS_NONE)
|
|
{
|
|
if ((newMode & mode) == 0)
|
|
{
|
|
return Value_new_ERROR(value, WRONGMODE);
|
|
}
|
|
|
|
mode = newMode;
|
|
++pc.token;
|
|
}
|
|
|
|
switch (pc.token->type)
|
|
{
|
|
case T_SHARED:
|
|
lock = FS_LOCK_NONE;
|
|
++pc.token;
|
|
break;
|
|
|
|
case T_LOCK_READ:
|
|
lock = FS_LOCK_SHARED;
|
|
++pc.token;
|
|
break;
|
|
|
|
case T_LOCK_WRITE:
|
|
lock = FS_LOCK_EXCLUSIVE;
|
|
++pc.token;
|
|
break;
|
|
|
|
default:;
|
|
}
|
|
|
|
if (pc.token->type != T_AS)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGAS);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
errpc = pc;
|
|
if (eval(&channelValue, _("channel"))->type == V_ERROR ||
|
|
Value_retype(&channelValue, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
pc = errpc;
|
|
Value_destroy(value);
|
|
*value = channelValue;
|
|
return value;
|
|
}
|
|
|
|
channel = channelValue.u.integer;
|
|
Value_destroy(&channelValue);
|
|
if (inout == 3)
|
|
{
|
|
if (pc.token->type == T_IDENTIFIER)
|
|
{
|
|
if (cistrcmp(pc.token->u.identifier->name, "len"))
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGLEN);
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_EQ)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGEQ);
|
|
}
|
|
|
|
++pc.token;
|
|
errpc = pc;
|
|
if (eval(&recLengthValue, _("record length"))->type == V_ERROR ||
|
|
Value_retype(&recLengthValue, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = recLengthValue;
|
|
return value;
|
|
}
|
|
|
|
recLength = recLengthValue.u.integer;
|
|
Value_destroy(&recLengthValue);
|
|
if (pass == INTERPRET && recLength <= 0)
|
|
{
|
|
Value_destroy(value);
|
|
pc = errpc;
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("record length"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
recLength = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* open file with name value */
|
|
if (pass == INTERPRET)
|
|
{
|
|
int res = -1;
|
|
|
|
if (inout == 0)
|
|
{
|
|
res = FS_openinChn(channel, value->u.string.character, mode);
|
|
}
|
|
else if (inout == 1)
|
|
{
|
|
res = FS_openoutChn(channel, value->u.string.character, mode, append);
|
|
}
|
|
else if (inout == 3)
|
|
{
|
|
res =
|
|
FS_openrandomChn(channel, value->u.string.character, mode,
|
|
recLength);
|
|
}
|
|
else if (inout == 4)
|
|
{
|
|
res = FS_openbinaryChn(channel, value->u.string.character, mode);
|
|
}
|
|
|
|
if (res == -1)
|
|
{
|
|
pc = statementpc;
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
else
|
|
{
|
|
if (lock != FS_LOCK_NONE && FS_lock(channel, 0, 0, lock, 0) == -1)
|
|
{
|
|
pc = statementpc;
|
|
Value_destroy(value);
|
|
Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
FS_close(channel);
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_OPTIONBASE(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("array subscript base"))->type == V_ERROR ||
|
|
(pass != DECLARE && Value_retype(value, V_INTEGER)->type == V_ERROR))
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
optionbase = value->u.integer;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_OPTIONRUN(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
FS_xonxoff(STDCHANNEL, 0);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_OPTIONSTOP(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
FS_xonxoff(STDCHANNEL, 1);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_OUT_POKE(struct Value *value)
|
|
{
|
|
int out, address, val;
|
|
struct Pc lpc;
|
|
|
|
out = (pc.token->type == T_OUT);
|
|
lpc = pc;
|
|
++pc.token;
|
|
if (eval(value, _("address"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
address = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("output value"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
val = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET)
|
|
{
|
|
if ((out ? FS_portOutput : FS_memOutput) (address, val) == -1)
|
|
{
|
|
pc = lpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_PRINT_LPRINT(struct Value *value)
|
|
{
|
|
int nl = 1;
|
|
int chn = (pc.token->type == T_PRINT ? STDCHANNEL : LPCHANNEL);
|
|
int printusing = 0;
|
|
struct Value usingval;
|
|
struct String *using = (struct String *)0;
|
|
size_t usingpos = 0;
|
|
|
|
++pc.token;
|
|
if (chn == STDCHANNEL && pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_USING)
|
|
{
|
|
struct Pc usingpc;
|
|
|
|
usingpc = pc;
|
|
printusing = 1;
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
if (pass == COMPILE &&
|
|
Program_imageLine(&program, pc.token->u.integer,
|
|
&usingpc.token->u.image) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHIMAGELINE);
|
|
}
|
|
else if (pass == INTERPRET)
|
|
{
|
|
using = usingpc.token->u.image.token->u.string;
|
|
}
|
|
|
|
Value_new_STRING(&usingval);
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
if (eval(&usingval, _("format string"))->type == V_ERROR ||
|
|
Value_retype(&usingval, V_STRING)->type == V_ERROR)
|
|
{
|
|
*value = usingval;
|
|
return value;
|
|
}
|
|
|
|
using = &usingval.u.string;
|
|
}
|
|
|
|
if (pc.token->type != T_SEMICOLON)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, MISSINGSEMICOLON);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
Value_new_STRING(&usingval);
|
|
using = &usingval.u.string;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
struct Pc valuepc;
|
|
|
|
valuepc = pc;
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
struct String s;
|
|
|
|
String_new(&s);
|
|
if (Value_toStringUsing(value, &s, using, &usingpos)->type ==
|
|
V_ERROR)
|
|
{
|
|
Value_destroy(&usingval);
|
|
String_destroy(&s);
|
|
pc = valuepc;
|
|
return value;
|
|
}
|
|
|
|
if (FS_putItem(chn, &s) == -1)
|
|
{
|
|
Value_destroy(&usingval);
|
|
Value_destroy(value);
|
|
String_destroy(&s);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
String_destroy(&s);
|
|
}
|
|
|
|
Value_destroy(value);
|
|
nl = 1;
|
|
}
|
|
else if (pc.token->type == T_TAB || pc.token->type == T_SPC)
|
|
{
|
|
int tab = pc.token->type == T_TAB;
|
|
|
|
++pc.token;
|
|
if (pc.token->type != T_OP)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, MISSINGOP);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("count"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
int s = value->u.integer;
|
|
int r = 0;
|
|
|
|
if (tab)
|
|
{
|
|
r = FS_tab(chn, s);
|
|
}
|
|
else
|
|
{
|
|
while (s-- > 0 && (r = FS_putChar(chn, ' ')) != -1);
|
|
}
|
|
|
|
if (r == -1)
|
|
{
|
|
Value_destroy(&usingval);
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
Value_destroy(value);
|
|
if (pc.token->type != T_CP)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, MISSINGCP);
|
|
}
|
|
|
|
++pc.token;
|
|
nl = 1;
|
|
}
|
|
|
|
else if (pc.token->type == T_SEMICOLON)
|
|
{
|
|
++pc.token;
|
|
nl = 0;
|
|
}
|
|
|
|
else if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (pass == INTERPRET && !printusing)
|
|
{
|
|
FS_nextcol(chn);
|
|
}
|
|
|
|
nl = 0;
|
|
}
|
|
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (pass == INTERPRET && FS_flush(chn) == -1)
|
|
{
|
|
Value_destroy(&usingval);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
Value_destroy(&usingval);
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (nl && FS_putChar(chn, '\n') == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (FS_flush(chn) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_RANDOMIZE(struct Value *value)
|
|
{
|
|
struct Pc argpc;
|
|
|
|
++pc.token;
|
|
argpc = pc;
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
Value_retype(value, V_INTEGER);
|
|
if (value->type == V_ERROR)
|
|
{
|
|
pc = argpc;
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, MISSINGEXPR,
|
|
_("random number generator seed"));
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
srand(pc.token->u.integer);
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
else
|
|
{
|
|
srand(getpid() ^ time((time_t *) 0));
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_READ(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
while (1)
|
|
{
|
|
struct Value *l;
|
|
struct Pc lvaluepc;
|
|
|
|
lvaluepc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGREADIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type ==
|
|
T_OP ? GLOBALARRAY : GLOBALVAR, 0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
if ((l = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET && dataread(value, l))
|
|
{
|
|
pc = lvaluepc;
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_COPY_RENAME(struct Value *value)
|
|
{
|
|
struct Pc argpc;
|
|
struct Value from;
|
|
struct Pc statementpc = pc;
|
|
|
|
++pc.token;
|
|
argpc = pc;
|
|
if (eval(&from, _("source file"))->type == V_ERROR ||
|
|
(pass != DECLARE && Value_retype(&from, V_STRING)->type == V_ERROR))
|
|
{
|
|
pc = argpc;
|
|
*value = from;
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type != T_TO)
|
|
{
|
|
Value_destroy(&from);
|
|
return Value_new_ERROR(value, MISSINGTO);
|
|
}
|
|
|
|
++pc.token;
|
|
argpc = pc;
|
|
if (eval(value, _("destination file"))->type == V_ERROR ||
|
|
(pass != DECLARE && Value_retype(value, V_STRING)->type == V_ERROR))
|
|
{
|
|
pc = argpc;
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
const char *msg;
|
|
int res;
|
|
|
|
if (statementpc.token->type == T_RENAME)
|
|
{
|
|
res = rename(from.u.string.character, value->u.string.character);
|
|
msg = strerror(errno);
|
|
}
|
|
else
|
|
{
|
|
res = FS_copy(from.u.string.character, value->u.string.character);
|
|
msg = FS_errmsg;
|
|
}
|
|
|
|
if (res == -1)
|
|
{
|
|
Value_destroy(&from);
|
|
Value_destroy(value);
|
|
pc = statementpc;
|
|
return Value_new_ERROR(value, IOERROR, msg);
|
|
}
|
|
}
|
|
|
|
Value_destroy(&from);
|
|
Value_destroy(value);
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_RENUM(struct Value *value)
|
|
{
|
|
int first = 10, inc = 10;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
first = pc.token->u.integer;
|
|
++pc.token;
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (pc.token->type != T_INTEGER)
|
|
return Value_new_ERROR(value, MISSINGINCREMENT);
|
|
inc = pc.token->u.integer;
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (!DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINPROGRAMMODE);
|
|
}
|
|
|
|
Program_renum(&program, first, inc);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_REPEAT(struct Value *value)
|
|
{
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
pushLabel(L_REPEAT, &pc);
|
|
}
|
|
|
|
++pc.token;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_RESTORE(struct Value *value)
|
|
{
|
|
struct Token *restorepc = pc.token;
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
curdata = pc.token->u.restore;
|
|
}
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
if (pass == COMPILE &&
|
|
Program_dataLine(&program, pc.token->u.integer,
|
|
&restorepc->u.restore) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHDATALINE);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
else if (pass == COMPILE)
|
|
{
|
|
restorepc->u.restore = stack.begindata;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_RETURN(struct Value *value)
|
|
{
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (Auto_gosubReturn(&stack, &pc))
|
|
{
|
|
Program_trace(&program, &pc, 0, 1);
|
|
}
|
|
else
|
|
{
|
|
return Value_new_ERROR(value, STRAYRETURN);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_RUN(struct Value *value)
|
|
{
|
|
struct Pc argpc, begin;
|
|
|
|
stack.resumeable = 0;
|
|
++pc.token;
|
|
argpc = pc;
|
|
if (pc.token->type == T_INTEGER)
|
|
{
|
|
if (Program_goLine(&program, pc.token->u.integer, &begin) ==
|
|
(struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOSUCHLINE);
|
|
}
|
|
|
|
if (pass == COMPILE &&
|
|
Program_scopeCheck(&program, &begin, findLabel(L_FUNC)))
|
|
{
|
|
return Value_new_ERROR(value, OUTOFSCOPE);
|
|
}
|
|
|
|
++pc.token;
|
|
}
|
|
else if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
pc = argpc;
|
|
return value;
|
|
}
|
|
else if (pass == INTERPRET)
|
|
{
|
|
int chn;
|
|
struct Program newprogram;
|
|
|
|
if ((chn = FS_openin(value->u.string.character)) == -1)
|
|
{
|
|
pc = argpc;
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
Value_destroy(value);
|
|
Program_new(&newprogram);
|
|
if (Program_merge(&newprogram, chn, value))
|
|
{
|
|
pc = argpc;
|
|
Program_destroy(&newprogram);
|
|
return value;
|
|
}
|
|
|
|
FS_close(chn);
|
|
new();
|
|
Program_destroy(&program);
|
|
program = newprogram;
|
|
if (Program_beginning(&program, &begin) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOPROGRAM);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Program_beginning(&program, &begin) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, NOPROGRAM);
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (compileProgram(value, 1)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
pc = begin;
|
|
curdata = stack.begindata;
|
|
Global_clear(&globals);
|
|
FS_closefiles();
|
|
Program_trace(&program, &pc, 0, 1);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_SAVE(struct Value *value)
|
|
{
|
|
struct Pc loadpc;
|
|
int name;
|
|
|
|
if (pass == INTERPRET && !DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINPROGRAMMODE);
|
|
}
|
|
|
|
++pc.token;
|
|
loadpc = pc;
|
|
if (pc.token->type == T_EOL && program.name.length)
|
|
{
|
|
name = 0;
|
|
}
|
|
else
|
|
{
|
|
name = 1;
|
|
if (eval(value, _("file name"))->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
pc = loadpc;
|
|
return value;
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
int chn;
|
|
|
|
if (name)
|
|
{
|
|
Program_setname(&program, value->u.string.character);
|
|
}
|
|
|
|
if ((chn = FS_openout(program.name.character)) == -1)
|
|
{
|
|
pc = loadpc;
|
|
if (name)
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
FS_width(chn, 0);
|
|
if (name)
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
if (Program_list(&program, chn, 0, (struct Pc *)0, (struct Pc *)0, value))
|
|
{
|
|
pc = loadpc;
|
|
return value;
|
|
}
|
|
|
|
FS_close(chn);
|
|
program.unsaved = 0;
|
|
}
|
|
else if (name)
|
|
{
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_SELECTCASE(struct Value *value)
|
|
{
|
|
struct Pc statementpc = pc;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
pushLabel(L_SELECTCASE, &pc);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("selector"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
statementpc.token->u.selectcase->type = value->type;
|
|
statementpc.token->u.selectcase->nextcasevalue.line = -1;
|
|
}
|
|
else
|
|
{
|
|
struct Pc casevaluepc;
|
|
int match = 0;
|
|
|
|
pc = casevaluepc = statementpc.token->u.selectcase->nextcasevalue;
|
|
do
|
|
{
|
|
++pc.token;
|
|
switch (casevaluepc.token->type)
|
|
{
|
|
case T_CASEVALUE:
|
|
{
|
|
do
|
|
{
|
|
struct Value casevalue1;
|
|
|
|
if (pc.token->type == T_IS)
|
|
{
|
|
enum TokenType relop;
|
|
|
|
++pc.token;
|
|
relop = pc.token->type;
|
|
++pc.token;
|
|
if (eval(&casevalue1, "`is'")->type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = casevalue1;
|
|
return value;
|
|
}
|
|
|
|
Value_retype(&casevalue1,
|
|
statementpc.token->u.selectcase->type);
|
|
assert(casevalue1.type != V_ERROR);
|
|
if (!match)
|
|
{
|
|
struct Value cmp;
|
|
|
|
Value_clone(&cmp, value);
|
|
switch (relop)
|
|
{
|
|
case T_LT:
|
|
Value_lt(&cmp, &casevalue1, 1);
|
|
break;
|
|
|
|
case T_LE:
|
|
Value_le(&cmp, &casevalue1, 1);
|
|
break;
|
|
|
|
case T_EQ:
|
|
Value_eq(&cmp, &casevalue1, 1);
|
|
break;
|
|
case T_GE:
|
|
Value_ge(&cmp, &casevalue1, 1);
|
|
break;
|
|
|
|
case T_GT:
|
|
Value_gt(&cmp, &casevalue1, 1);
|
|
break;
|
|
case T_NE:
|
|
Value_ne(&cmp, &casevalue1, 1);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
assert(cmp.type == V_INTEGER);
|
|
match = cmp.u.integer;
|
|
Value_destroy(&cmp);
|
|
}
|
|
|
|
Value_destroy(&casevalue1);
|
|
}
|
|
else
|
|
{
|
|
if (eval(&casevalue1, "`case'")->type == V_ERROR)
|
|
{
|
|
Value_destroy(value);
|
|
*value = casevalue1;
|
|
return value;
|
|
}
|
|
|
|
Value_retype(&casevalue1,
|
|
statementpc.token->u.selectcase->type);
|
|
assert(casevalue1.type != V_ERROR);
|
|
if (pc.token->type == T_TO) /* match range */
|
|
{
|
|
struct Value casevalue2;
|
|
|
|
++pc.token;
|
|
if (eval(&casevalue2, "`case'")->type == V_ERROR)
|
|
{
|
|
Value_destroy(&casevalue1);
|
|
Value_destroy(value);
|
|
*value = casevalue2;
|
|
return value;
|
|
}
|
|
|
|
Value_retype(&casevalue2,
|
|
statementpc.token->u.selectcase->type);
|
|
assert(casevalue2.type != V_ERROR);
|
|
if (!match)
|
|
{
|
|
struct Value cmp1, cmp2;
|
|
|
|
Value_clone(&cmp1, value);
|
|
Value_clone(&cmp2, value);
|
|
Value_ge(&cmp1, &casevalue1, 1);
|
|
assert(cmp1.type == V_INTEGER);
|
|
Value_le(&cmp2, &casevalue2, 1);
|
|
assert(cmp2.type == V_INTEGER);
|
|
match = cmp1.u.integer && cmp2.u.integer;
|
|
Value_destroy(&cmp1);
|
|
Value_destroy(&cmp2);
|
|
}
|
|
|
|
Value_destroy(&casevalue2);
|
|
}
|
|
else /* match value */
|
|
{
|
|
if (!match)
|
|
{
|
|
struct Value cmp;
|
|
|
|
Value_clone(&cmp, value);
|
|
Value_eq(&cmp, &casevalue1, 1);
|
|
assert(cmp.type == V_INTEGER);
|
|
match = cmp.u.integer;
|
|
Value_destroy(&cmp);
|
|
}
|
|
}
|
|
|
|
Value_destroy(&casevalue1);
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while (1);
|
|
|
|
break;
|
|
}
|
|
|
|
case T_CASEELSE:
|
|
{
|
|
match = 1;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
if (!match)
|
|
{
|
|
if (casevaluepc.token->u.casevalue->nextcasevalue.line != -1)
|
|
{
|
|
pc = casevaluepc =
|
|
casevaluepc.token->u.casevalue->nextcasevalue;
|
|
}
|
|
else
|
|
{
|
|
pc = statementpc.token->u.selectcase->endselect;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (!match);
|
|
}
|
|
|
|
Value_destroy(value);
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_SHELL(struct Value *value)
|
|
{
|
|
#ifdef CONFIG_ARCH_HAVE_VFORK
|
|
pid_t pid;
|
|
int status;
|
|
|
|
++pc.token;
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR ||
|
|
Value_retype(value, V_STRING)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (run_restricted)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, RESTRICTED, strerror(errno));
|
|
}
|
|
|
|
FS_shellmode(STDCHANNEL);
|
|
switch (pid = vfork())
|
|
{
|
|
case -1:
|
|
{
|
|
FS_fsmode(STDCHANNEL);
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, FORKFAILED, strerror(errno));
|
|
}
|
|
|
|
case 0:
|
|
{
|
|
execl("/bin/sh", "sh", "-c", value->u.string.character,
|
|
(const char *)0);
|
|
exit(127);
|
|
}
|
|
|
|
default:
|
|
{
|
|
/* Wait for the shell to complete */
|
|
|
|
while (waitpid(pid, &status, 0) < 0 && errno != EINTR);
|
|
}
|
|
}
|
|
|
|
FS_fsmode(STDCHANNEL);
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
else
|
|
{
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (run_restricted)
|
|
{
|
|
return Value_new_ERROR(value, RESTRICTED, strerror(errno));
|
|
}
|
|
|
|
FS_shellmode(STDCHANNEL);
|
|
switch (pid = vfork())
|
|
{
|
|
case -1:
|
|
{
|
|
FS_fsmode(STDCHANNEL);
|
|
return Value_new_ERROR(value, FORKFAILED, strerror(errno));
|
|
}
|
|
|
|
case 0:
|
|
{
|
|
const char *shell;
|
|
|
|
shell = getenv("SHELL");
|
|
if (shell == (const char *)0)
|
|
{
|
|
shell = "/bin/sh";
|
|
}
|
|
|
|
execl(shell,
|
|
(strrchr(shell, '/') ? strrchr(shell, '/') + 1 : shell),
|
|
(const char *)0);
|
|
exit(127);
|
|
}
|
|
|
|
default:
|
|
{
|
|
/* Wait for the shell to complete */
|
|
|
|
while (waitpid(pid, &status, 0) < 0 && errno != EINTR);
|
|
}
|
|
}
|
|
|
|
FS_fsmode(STDCHANNEL);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
#else
|
|
return Value_new_ERROR(value, FORKFAILED, strerror(ENOSYS));
|
|
#endif
|
|
}
|
|
|
|
struct Value *stmt_SLEEP(struct Value *value)
|
|
{
|
|
double s;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("pause"))->type == V_ERROR ||
|
|
Value_retype(value, V_REAL)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
s = value->u.real;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (s < 0.0)
|
|
{
|
|
return Value_new_ERROR(value, OUTOFRANGE, _("pause"));
|
|
}
|
|
|
|
FS_sleep(s);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_STOP(struct Value *value)
|
|
{
|
|
if (pass != INTERPRET)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_SUBEXIT(struct Value *value)
|
|
{
|
|
struct Pc *curfn = (struct Pc *)0;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((curfn = findLabel(L_FUNC)) == (struct Pc *)0 ||
|
|
(curfn->token + 1)->u.identifier->defaultType != V_VOID)
|
|
{
|
|
return Value_new_ERROR(value, STRAYSUBEXIT);
|
|
}
|
|
}
|
|
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
return Value_new_VOID(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_SWAP(struct Value *value)
|
|
{
|
|
struct Value *l1, *l2;
|
|
struct Pc lvaluepc;
|
|
|
|
++pc.token;
|
|
lvaluepc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGSWAPIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type == T_OP ? GLOBALARRAY : GLOBALVAR,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
if ((l1 = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
lvaluepc = pc;
|
|
if (pc.token->type != T_IDENTIFIER)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGSWAPIDENT);
|
|
}
|
|
|
|
if (pass == DECLARE &&
|
|
Global_variable(&globals, pc.token->u.identifier,
|
|
pc.token->u.identifier->defaultType,
|
|
(pc.token + 1)->type == T_OP ? GLOBALARRAY : GLOBALVAR,
|
|
0) == 0)
|
|
{
|
|
return Value_new_ERROR(value, REDECLARATION);
|
|
}
|
|
|
|
if ((l2 = lvalue(value))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (l1->type != l2->type)
|
|
{
|
|
pc = lvaluepc;
|
|
return Value_new_typeError(value, l2->type, l1->type);
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
struct Value foo;
|
|
|
|
foo = *l1;
|
|
*l1 = *l2;
|
|
*l2 = foo;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_SYSTEM(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (program.unsaved)
|
|
{
|
|
int ch;
|
|
|
|
FS_putChars(STDCHANNEL, _("Quit without saving? (y/n) "));
|
|
FS_flush(STDCHANNEL);
|
|
if ((ch = FS_getChar(STDCHANNEL)) != -1)
|
|
{
|
|
FS_putChar(STDCHANNEL, ch);
|
|
FS_flush(STDCHANNEL);
|
|
FS_nextline(STDCHANNEL);
|
|
if (tolower(ch) == *_("yes"))
|
|
{
|
|
bas_exit();
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bas_exit();
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_TROFF(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
program.trace = 0;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_TRON(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
program.trace = 1;
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_TRUNCATE(struct Value *value)
|
|
{
|
|
struct Pc chnpc;
|
|
int chn;
|
|
|
|
chnpc = pc;
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
}
|
|
|
|
if (eval(value, (const char *)0) == (struct Value *)0)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGEXPR, _("channel"));
|
|
}
|
|
|
|
if (value->type == V_ERROR || Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && FS_truncate(chn) == -1)
|
|
{
|
|
pc = chnpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_UNNUM(struct Value *value)
|
|
{
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (!DIRECTMODE)
|
|
{
|
|
return Value_new_ERROR(value, NOTINPROGRAMMODE);
|
|
}
|
|
|
|
Program_unnum(&program);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_UNTIL(struct Value *value)
|
|
{
|
|
struct Pc untilpc = pc;
|
|
struct Pc *repeatpc;
|
|
|
|
++pc.token;
|
|
if (eval(value, _("condition"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (Value_isNull(value))
|
|
{
|
|
pc = untilpc.token->u.until;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
if ((repeatpc = popLabel(L_REPEAT)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYUNTIL);
|
|
}
|
|
|
|
untilpc.token->u.until = *repeatpc;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_WAIT(struct Value *value)
|
|
{
|
|
int address, mask, sel = -1, usesel;
|
|
struct Pc lpc;
|
|
|
|
lpc = pc;
|
|
++pc.token;
|
|
if (eval(value, _("address"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
address = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type != T_COMMA)
|
|
{
|
|
return Value_new_ERROR(value, MISSINGCOMMA);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("mask"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
mask = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("select"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
sel = value->u.integer;
|
|
usesel = 1;
|
|
Value_destroy(value);
|
|
}
|
|
else
|
|
{
|
|
usesel = 0;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
int v;
|
|
|
|
do
|
|
{
|
|
if ((v = FS_portInput(address)) == -1)
|
|
{
|
|
pc = lpc;
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
while ((usesel ? (v ^ sel) & mask : v ^ mask) == 0);
|
|
}
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_WHILE(struct Value *value)
|
|
{
|
|
struct Pc whilepc = pc;
|
|
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
pushLabel(L_WHILE, &pc);
|
|
}
|
|
|
|
++pc.token;
|
|
if (eval(value, _("condition"))->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (Value_isNull(value))
|
|
{
|
|
pc = *whilepc.token->u.afterwend;
|
|
}
|
|
|
|
Value_destroy(value);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_WEND(struct Value *value)
|
|
{
|
|
if (pass == DECLARE || pass == COMPILE)
|
|
{
|
|
struct Pc *whilepc;
|
|
|
|
if ((whilepc = popLabel(L_WHILE)) == (struct Pc *)0)
|
|
{
|
|
return Value_new_ERROR(value, STRAYWEND, topLabelDescription());
|
|
}
|
|
|
|
*pc.token->u.whilepc = *whilepc;
|
|
++pc.token;
|
|
*(whilepc->token->u.afterwend) = pc;
|
|
}
|
|
else
|
|
{
|
|
pc = *pc.token->u.whilepc;
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_WIDTH(struct Value *value)
|
|
{
|
|
int chn = STDCHANNEL, width;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
width = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && FS_width(chn, width) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("zone width"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
width = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && FS_zone(chn, width) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_WRITE(struct Value *value)
|
|
{
|
|
int chn = STDCHANNEL;
|
|
int comma = 0;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (eval(value, (const char *)0))
|
|
{
|
|
if (value->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
struct String s;
|
|
|
|
String_new(&s);
|
|
if (comma)
|
|
{
|
|
String_appendChar(&s, ',');
|
|
}
|
|
|
|
if (FS_putString(chn, Value_toWrite(value, &s)) == -1)
|
|
{
|
|
Value_destroy(value);
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
if (FS_flush(chn) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
String_destroy(&s);
|
|
}
|
|
|
|
Value_destroy(value);
|
|
comma = 1;
|
|
}
|
|
else if (pc.token->type == T_COMMA || pc.token->type == T_SEMICOLON)
|
|
{
|
|
++pc.token;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pass == INTERPRET)
|
|
{
|
|
FS_putChar(chn, '\n');
|
|
if (FS_flush(chn) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_XREF(struct Value *value)
|
|
{
|
|
stack.resumeable = 0;
|
|
++pc.token;
|
|
if (pass == INTERPRET)
|
|
{
|
|
if (!program.runnable && compileProgram(value, 1)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
Program_xref(&program, STDCHANNEL);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|
|
|
|
struct Value *stmt_ZONE(struct Value *value)
|
|
{
|
|
int chn = STDCHANNEL, width;
|
|
|
|
++pc.token;
|
|
if (pc.token->type == T_CHANNEL)
|
|
{
|
|
++pc.token;
|
|
if (eval(value, _("channel"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
chn = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pc.token->type == T_COMMA)
|
|
{
|
|
++pc.token;
|
|
}
|
|
}
|
|
|
|
if (eval(value, _("zone width"))->type == V_ERROR ||
|
|
Value_retype(value, V_INTEGER)->type == V_ERROR)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
width = value->u.integer;
|
|
Value_destroy(value);
|
|
if (pass == INTERPRET && FS_zone(chn, width) == -1)
|
|
{
|
|
return Value_new_ERROR(value, IOERROR, FS_errmsg);
|
|
}
|
|
|
|
return (struct Value *)0;
|
|
}
|