mirror of
https://github.com/lcn2/calc.git
synced 2025-08-19 01:13:27 +03:00
While help/errstr has been added, the errstr builtin function is not yet written. In anticipation of the new errstr builtin the rest of the calc error system has been updated to associated errsym E_STRING's with errnum error codes and errmsg error messages. Minor improvements to help/rand. The verify_error_table() function that does a verification the error_table[] array and setup private_error_alias[] array is now called by libcalc_call_me_first(). Fix comment about wrong include file in have_sys_mount.h. Removed unused booltostr() and strtobool() macros from bool.h. Moved define of math_error(char *, ...) from zmath.h to errtbl.h. The errtbl.h include file, unless ERRCODE_SRC is defined also includes attribute.h and errsym.h. Group calc error related builtin support functions together in func.c. Make switch indenting in func.c consistent. Passing an invalid argument to error(), errno() or strerror() will set errno AND throw a math error. Before errno would be set and an error value was returned. Before there was no way to tell if the error value was a result of the arg or if an error detected. Added E_STRING to error([errnum | "E_STRING"]) builtin function. Added E_STRING to errno([errnum | "E_STRING"]) builtin function. Added E_STRING to strerror([errnum | "E_STRING"]) builtin function. Calling these functions with an E_STRING errsym is the same as calling them with the matching errnum code. Standardized on calc computation error related E_STRING strings where there are a set of related codes. Changed "E_...digits" into "E_..._digits". For example, E_FPUTC1 became E_FPUTC_1, E_FPUTC2 became E_FPUTC_2, and E_FPUTC3 became E_FPUTC_3. In a few cases such as E_APPR became E_APPR_1, because there was a E_APPR2 (which became E_APPR_2) and E_APPR3 (which became E_APPR_3). To other special cases, E_ILOG10 became E_IBASE10_LOG and E_ILOG2 became E_IBASE2_LOG because E_ILOG10 and E_ILOG2 are both independent calc computation error related E_STRING strings. Now related sets of E_STRING strings end in _ (underscore) followed by digits. The following is the list of E_STRING strings changes: E_APPR ==> E_APPR_1 E_ROUND ==> E_ROUND_1 E_SQRT ==> E_SQRT_1 E_ROOT ==> E_ROOT_1 E_SHIFT ==> E_SHIFT_1 E_SCALE ==> E_SCALE_1 E_POWI ==> E_POWI_1 E_POWER ==> E_POWER_1 E_QUO ==> E_QUO_1 E_MOD ==> E_MOD_1 E_ABS ==> E_ABS_1 E_APPR2 ==> E_APPR_2 E_APPR3 ==> E_APPR_3 E_ROUND2 ==> E_ROUND_2 E_ROUND3 ==> E_ROUND_3 E_BROUND2 ==> E_BROUND_2 E_BROUND3 ==> E_BROUND_3 E_SQRT2 ==> E_SQRT_2 E_SQRT3 ==> E_SQRT_3 E_ROOT2 ==> E_ROOT_2 E_ROOT3 ==> E_ROOT_3 E_SHIFT2 ==> E_SHIFT_2 E_SCALE2 ==> E_SCALE_2 E_POWI2 ==> E_POWI_2 E_POWER2 ==> E_POWER_2 E_POWER3 ==> E_POWER_3 E_QUO2 ==> E_QUO_2 E_QUO3 ==> E_QUO_3 E_MOD2 ==> E_MOD_2 E_MOD3 ==> E_MOD_3 E_ABS2 ==> E_ABS_2 E_EXP1 ==> E_EXP_1 E_EXP2 ==> E_EXP_2 E_FPUTC1 ==> E_FPUTC_1 E_FPUTC2 ==> E_FPUTC_2 E_FPUTC3 ==> E_FPUTC_3 E_FGETC1 ==> E_FGETC_1 E_FGETC2 ==> E_FGETC_2 E_FOPEN1 ==> E_FOPEN_1 E_FOPEN2 ==> E_FOPEN_2 E_FREOPEN1 ==> E_FREOPEN_1 E_FREOPEN2 ==> E_FREOPEN_2 E_FREOPEN3 ==> E_FREOPEN_3 E_FCLOSE1 ==> E_FCLOSE_1 E_FPUTS1 ==> E_FPUTS_1 E_FPUTS2 ==> E_FPUTS_2 E_FPUTS3 ==> E_FPUTS_3 E_FGETS1 ==> E_FGETS_1 E_FGETS2 ==> E_FGETS_2 E_FPUTSTR1 ==> E_FPUTSTR_1 E_FPUTSTR2 ==> E_FPUTSTR_2 E_FPUTSTR3 ==> E_FPUTSTR_3 E_FGETSTR1 ==> E_FGETSTR_1 E_FGETSTR2 ==> E_FGETSTR_2 E_FGETLINE1 ==> E_FGETLINE_1 E_FGETLINE2 ==> E_FGETLINE_2 E_FGETFIELD1 ==> E_FGETFIELD_1 E_FGETFIELD2 ==> E_FGETFIELD_2 E_REWIND1 ==> E_REWIND_1 E_PRINTF1 ==> E_PRINTF_1 E_PRINTF2 ==> E_PRINTF_2 E_FPRINTF1 ==> E_FPRINTF_1 E_FPRINTF2 ==> E_FPRINTF_2 E_FPRINTF3 ==> E_FPRINTF_3 E_STRPRINTF1 ==> E_STRPRINTF_1 E_STRPRINTF2 ==> E_STRPRINTF_2 E_FSCAN1 ==> E_FSCAN_1 E_FSCAN2 ==> E_FSCAN_2 E_FSCANF1 ==> E_FSCANF_1 E_FSCANF2 ==> E_FSCANF_2 E_FSCANF3 ==> E_FSCANF_3 E_FSCANF4 ==> E_FSCANF_4 E_STRSCANF1 ==> E_STRSCANF_1 E_STRSCANF2 ==> E_STRSCANF_2 E_STRSCANF3 ==> E_STRSCANF_3 E_STRSCANF4 ==> E_STRSCANF_4 E_SCANF1 ==> E_SCANF_1 E_SCANF2 ==> E_SCANF_2 E_SCANF3 ==> E_SCANF_3 E_FTELL1 ==> E_FTELL_1 E_FTELL2 ==> E_FTELL_2 E_FSEEK1 ==> E_FSEEK_1 E_FSEEK2 ==> E_FSEEK_2 E_FSEEK3 ==> E_FSEEK_3 E_FSIZE1 ==> E_FSIZE_1 E_FSIZE2 ==> E_FSIZE_2 E_FEOF1 ==> E_FEOF_1 E_FEOF2 ==> E_FEOF_2 E_FERROR1 ==> E_FERROR_1 E_FERROR2 ==> E_FERROR_2 E_UNGETC1 ==> E_UNGETC_1 E_UNGETC2 ==> E_UNGETC_2 E_UNGETC3 ==> E_UNGETC_3 E_ISATTY1 ==> E_ISATTY_1 E_ISATTY2 ==> E_ISATTY_2 E_ACCESS1 ==> E_ACCESS_1 E_ACCESS2 ==> E_ACCESS_2 E_SEARCH1 ==> E_SEARCH_1 E_SEARCH2 ==> E_SEARCH_2 E_SEARCH3 ==> E_SEARCH_3 E_SEARCH4 ==> E_SEARCH_4 E_SEARCH5 ==> E_SEARCH_5 E_SEARCH6 ==> E_SEARCH_6 E_RSEARCH1 ==> E_RSEARCH_1 E_RSEARCH2 ==> E_RSEARCH_2 E_RSEARCH3 ==> E_RSEARCH_3 E_RSEARCH4 ==> E_RSEARCH_4 E_RSEARCH5 ==> E_RSEARCH_5 E_RSEARCH6 ==> E_RSEARCH_6 E_REWIND2 ==> E_REWIND_2 E_STRERROR1 ==> E_STRERROR_1 E_STRERROR2 ==> E_STRERROR_2 E_COS1 ==> E_COS_1 E_COS2 ==> E_COS_2 E_SIN1 ==> E_SIN_1 E_SIN2 ==> E_SIN_2 E_EVAL2 ==> E_EVAL_2 E_ARG1 ==> E_ARG_1 E_ARG2 ==> E_ARG_2 E_POLAR1 ==> E_POLAR_1 E_POLAR2 ==> E_POLAR_2 E_MATFILL1 ==> E_MATFILL_1 E_MATFILL2 ==> E_MATFILL_2 E_MATTRANS1 ==> E_MATTRANS_1 E_MATTRANS2 ==> E_MATTRANS_2 E_DET1 ==> E_DET_1 E_DET2 ==> E_DET_2 E_DET3 ==> E_DET_3 E_MATMIN1 ==> E_MATMIN_1 E_MATMIN2 ==> E_MATMIN_2 E_MATMIN3 ==> E_MATMIN_3 E_MATMAX1 ==> E_MATMAX_1 E_MATMAX2 ==> E_MATMAX_2 E_MATMAX3 ==> E_MATMAX_3 E_CP1 ==> E_CP_1 E_CP2 ==> E_CP_2 E_CP3 ==> E_CP_3 E_DP1 ==> E_DP_1 E_DP2 ==> E_DP_2 E_DP3 ==> E_DP_3 E_SUBSTR1 ==> E_SUBSTR_1 E_SUBSTR2 ==> E_SUBSTR_2 E_INSERT1 ==> E_INSERT_1 E_INSERT2 ==> E_INSERT_2 E_DELETE1 ==> E_DELETE_1 E_DELETE2 ==> E_DELETE_2 E_LN1 ==> E_LN_1 E_LN2 ==> E_LN_2 E_ERROR1 ==> E_ERROR_1 E_ERROR2 ==> E_ERROR_2 E_EVAL3 ==> E_EVAL_3 E_EVAL4 ==> E_EVAL_4 E_RM1 ==> E_RM_1 E_RM2 ==> E_RM_2 E_BLK1 ==> E_BLK_1 E_BLK2 ==> E_BLK_2 E_BLK3 ==> E_BLK_3 E_BLK4 ==> E_BLK_4 E_BLKFREE1 ==> E_BLKFREE_1 E_BLKFREE2 ==> E_BLKFREE_2 E_BLKFREE3 ==> E_BLKFREE_3 E_BLKFREE4 ==> E_BLKFREE_4 E_BLKFREE5 ==> E_BLKFREE_5 E_BLOCKS1 ==> E_BLOCKS_1 E_BLOCKS2 ==> E_BLOCKS_2 E_COPY1 ==> E_COPY_01 E_COPY2 ==> E_COPY_02 E_COPY3 ==> E_COPY_03 E_COPY4 ==> E_COPY_04 E_COPY5 ==> E_COPY_05 E_COPY6 ==> E_COPY_06 E_COPY7 ==> E_COPY_07 E_COPY8 ==> E_COPY_08 E_COPY9 ==> E_COPY_09 E_COPY10 ==> E_COPY_10 E_COPY11 ==> E_COPY_11 E_COPY12 ==> E_COPY_12 E_COPY13 ==> E_COPY_13 E_COPY14 ==> E_COPY_14 E_COPY15 ==> E_COPY_15 E_COPY16 ==> E_COPY_16 E_COPY17 ==> E_COPY_17 E_COPYF1 ==> E_COPYF_1 E_COPYF2 ==> E_COPYF_2 E_COPYF3 ==> E_COPYF_3 E_COPYF4 ==> E_COPYF_4 E_PROTECT1 ==> E_PROTECT_1 E_PROTECT2 ==> E_PROTECT_2 E_PROTECT3 ==> E_PROTECT_3 E_MATFILL3 ==> E_MATFILL_3 E_MATFILL4 ==> E_MATFILL_4 E_MATTRACE1 ==> E_MATTRACE_1 E_MATTRACE2 ==> E_MATTRACE_2 E_MATTRACE3 ==> E_MATTRACE_3 E_TAN1 ==> E_TAN_1 E_TAN2 ==> E_TAN_2 E_COT1 ==> E_COT_1 E_COT2 ==> E_COT_2 E_SEC1 ==> E_SEC_1 E_SEC2 ==> E_SEC_2 E_CSC1 ==> E_CSC_1 E_CSC2 ==> E_CSC_2 E_SINH1 ==> E_SINH_1 E_SINH2 ==> E_SINH_2 E_COSH1 ==> E_COSH_1 E_COSH2 ==> E_COSH_2 E_TANH1 ==> E_TANH_1 E_TANH2 ==> E_TANH_2 E_COTH1 ==> E_COTH_1 E_COTH2 ==> E_COTH_2 E_SECH1 ==> E_SECH_1 E_SECH2 ==> E_SECH_2 E_CSCH1 ==> E_CSCH_1 E_CSCH2 ==> E_CSCH_2 E_ASIN1 ==> E_ASIN_1 E_ASIN2 ==> E_ASIN_2 E_ACOS1 ==> E_ACOS_1 E_ACOS2 ==> E_ACOS_2 E_ATAN1 ==> E_ATAN_1 E_ATAN2 ==> E_ATAN_2 E_ACOT1 ==> E_ACOT_1 E_ACOT2 ==> E_ACOT_2 E_ASEC1 ==> E_ASEC_1 E_ASEC2 ==> E_ASEC_2 E_ACSC1 ==> E_ACSC_1 E_ACSC2 ==> E_ACSC_2 E_ASINH1 ==> E_ASINH_1 E_ASINH2 ==> E_ASINH_2 E_ACOSH1 ==> E_ACOSH_1 E_ACOSH2 ==> E_ACOSH_2 E_ATANH1 ==> E_ATANH_1 E_ATANH2 ==> E_ATANH_2 E_ACOTH1 ==> E_ACOTH_1 E_ACOTH2 ==> E_ACOTH_2 E_ASECH1 ==> E_ASECH_1 E_ASECH2 ==> E_ASECH_2 E_ACSCH1 ==> E_ACSCH_1 E_ACSCH2 ==> E_ACSCH_2 E_GD1 ==> E_GD_1 E_GD2 ==> E_GD_2 E_AGD1 ==> E_AGD_1 E_AGD2 ==> E_AGD_2 E_BIT1 ==> E_BIT_1 E_BIT2 ==> E_BIT_2 E_SETBIT1 ==> E_SETBIT_1 E_SETBIT2 ==> E_SETBIT_2 E_SETBIT3 ==> E_SETBIT_3 E_SEG1 ==> E_SEG_1 E_SEG2 ==> E_SEG_2 E_SEG3 ==> E_SEG_3 E_HIGHBIT1 ==> E_HIGHBIT_1 E_HIGHBIT2 ==> E_HIGHBIT_2 E_LOWBIT1 ==> E_LOWBIT_1 E_LOWBIT2 ==> E_LOWBIT_2 E_HEAD1 ==> E_HEAD_1 E_HEAD2 ==> E_HEAD_2 E_TAIL1 ==> E_TAIL_1 E_TAIL2 ==> E_TAIL_2 E_XOR1 ==> E_XOR_1 E_XOR2 ==> E_XOR_2 E_INDICES1 ==> E_INDICES_1 E_INDICES2 ==> E_INDICES_2 E_EXP3 ==> E_EXP_3 E_SINH3 ==> E_SINH_3 E_COSH3 ==> E_COSH_3 E_SIN3 ==> E_SIN_3 E_COS3 ==> E_COS_3 E_GD3 ==> E_GD_3 E_AGD3 ==> E_AGD_3 E_POWER4 ==> E_POWER_4 E_ROOT4 ==> E_ROOT_4 E_DGT1 ==> E_DGT_1 E_DGT2 ==> E_DGT_2 E_DGT3 ==> E_DGT_3 E_PLCS1 ==> E_PLCS_1 E_PLCS2 ==> E_PLCS_2 E_DGTS1 ==> E_DGTS_1 E_DGTS2 ==> E_DGTS_2 E_ILOG10 ==> E_IBASE10_LOG E_ILOG2 ==> E_IBASE2_LOG E_COMB1 ==> E_COMB_1 E_COMB2 ==> E_COMB_2 E_ASSIGN1 ==> E_ASSIGN_1 E_ASSIGN2 ==> E_ASSIGN_2 E_ASSIGN3 ==> E_ASSIGN_3 E_ASSIGN4 ==> E_ASSIGN_4 E_ASSIGN5 ==> E_ASSIGN_5 E_ASSIGN6 ==> E_ASSIGN_6 E_ASSIGN7 ==> E_ASSIGN_7 E_ASSIGN8 ==> E_ASSIGN_8 E_ASSIGN9 ==> E_ASSIGN_9 E_SWAP1 ==> E_SWAP_1 E_SWAP2 ==> E_SWAP_2 E_SWAP3 ==> E_SWAP_3 E_QUOMOD1 ==> E_QUOMOD_1 E_QUOMOD2 ==> E_QUOMOD_2 E_QUOMOD3 ==> E_QUOMOD_3 E_PREINC1 ==> E_PREINC_1 E_PREINC2 ==> E_PREINC_2 E_PREINC3 ==> E_PREINC_3 E_PREDEC1 ==> E_PREDEC_1 E_PREDEC2 ==> E_PREDEC_2 E_PREDEC3 ==> E_PREDEC_3 E_POSTINC1 ==> E_POSTINC_1 E_POSTINC2 ==> E_POSTINC_2 E_POSTINC3 ==> E_POSTINC_3 E_POSTDEC1 ==> E_POSTDEC_1 E_POSTDEC2 ==> E_POSTDEC_2 E_POSTDEC3 ==> E_POSTDEC_3 E_INIT1 ==> E_INIT_01 E_INIT2 ==> E_INIT_02 E_INIT3 ==> E_INIT_03 E_INIT4 ==> E_INIT_04 E_INIT5 ==> E_INIT_05 E_INIT6 ==> E_INIT_06 E_INIT7 ==> E_INIT_07 E_INIT8 ==> E_INIT_08 E_INIT9 ==> E_INIT_09 E_INIT10 ==> E_INIT_10 E_LIST1 ==> E_LIST_1 E_LIST2 ==> E_LIST_2 E_LIST3 ==> E_LIST_3 E_LIST4 ==> E_LIST_4 E_LIST5 ==> E_LIST_5 E_LIST6 ==> E_LIST_6 E_MODIFY1 ==> E_MODIFY_1 E_MODIFY2 ==> E_MODIFY_2 E_MODIFY3 ==> E_MODIFY_3 E_MODIFY4 ==> E_MODIFY_4 E_MODIFY5 ==> E_MODIFY_5 E_FPATHOPEN1 ==> E_FPATHOPEN_1 E_FPATHOPEN2 ==> E_FPATHOPEN_2 E_LOG1 ==> E_LOG_1 E_LOG2 ==> E_LOG_2 E_LOG3 ==> E_LOG_3 E_FGETFILE1 ==> E_FGETFILE_1 E_FGETFILE2 ==> E_FGETFILE_2 E_FGETFILE3 ==> E_FGETFILE_3 E_TAN3 ==> E_TAN_3 E_TAN4 ==> E_TAN_4 E_COT3 ==> E_COT_3 E_COT4 ==> E_COT_4 E_SEC3 ==> E_SEC_3 E_CSC3 ==> E_CSC_3 E_TANH3 ==> E_TANH_3 E_TANH4 ==> E_TANH_4 E_COTH3 ==> E_COTH_3 E_COTH4 ==> E_COTH_4 E_SECH3 ==> E_SECH_3 E_CSCH3 ==> E_CSCH_3 E_ASIN3 ==> E_ASIN_3 E_ACOS3 ==> E_ACOS_3 E_ASINH3 ==> E_ASINH_3 E_ACOSH3 ==> E_ACOSH_3 E_ATAN3 ==> E_ATAN_3 E_ACOT3 ==> E_ACOT_3 E_ASEC3 ==> E_ASEC_3 E_ACSC3 ==> E_ACSC_3 E_ATANH3 ==> E_ATANH_3 E_ACOTH3 ==> E_ACOTH_3 E_ASECH3 ==> E_ASECH_3 E_ACSCH3 ==> E_ACSCH_3 E_D2R1 ==> E_D2R_1 E_D2R2 ==> E_D2R_2 E_R2D1 ==> E_R2D_1 E_R2D2 ==> E_R2D_2 E_G2R1 ==> E_G2R_1 E_G2R2 ==> E_G2R_2 E_R2G1 ==> E_R2G_1 E_R2G2 ==> E_R2G_2 E_D2G1 ==> E_D2G_1 E_G2D1 ==> E_G2D_1 E_D2DMS1 ==> E_D2DMS_1 E_D2DMS2 ==> E_D2DMS_2 E_D2DMS3 ==> E_D2DMS_3 E_D2DMS4 ==> E_D2DMS_4 E_D2DM1 ==> E_D2DM_1 E_D2DM2 ==> E_D2DM_2 E_D2DM3 ==> E_D2DM_3 E_D2DM4 ==> E_D2DM_4 E_G2GMS1 ==> E_G2GMS_1 E_G2GMS2 ==> E_G2GMS_2 E_G2GMS3 ==> E_G2GMS_3 E_G2GMS4 ==> E_G2GMS_4 E_G2GM1 ==> E_G2GM_1 E_G2GM2 ==> E_G2GM_2 E_G2GM3 ==> E_G2GM_3 E_G2GM4 ==> E_G2GM_4 E_H2HMS1 ==> E_H2HMS_1 E_H2HMS2 ==> E_H2HMS_2 E_H2HMS3 ==> E_H2HMS_3 E_H2HMS4 ==> E_H2HMS_4 E_H2HM1 ==> E_H2HM_1 E_H2HM2 ==> E_H2HM_2 E_H2HM3 ==> E_H2HM_3 E_H2HM4 ==> E_H2HM_4 E_DMS2D1 ==> E_DMS2D_1 E_DMS2D2 ==> E_DMS2D_2 E_DM2D1 ==> E_DM2D_1 E_DM2D2 ==> E_DM2D_2 E_GMS2G1 ==> E_GMS2G_1 E_GMS2G2 ==> E_GMS2G_2 E_GM2G1 ==> E_GM2G_1 E_GM2G2 ==> E_GM2G_2 E_HMS2H1 ==> E_HMS2H_1 E_HMS2H2 ==> E_HMS2H_2 E_HM2H1 ==> E_HM2H_1 E_HM2H2 ==> E_HM2H_2 E_VERSIN1 ==> E_VERSIN_1 E_VERSIN2 ==> E_VERSIN_2 E_VERSIN3 ==> E_VERSIN_3 E_AVERSIN1 ==> E_AVERSIN_1 E_AVERSIN2 ==> E_AVERSIN_2 E_AVERSIN3 ==> E_AVERSIN_3 E_COVERSIN1 ==> E_COVERSIN_1 E_COVERSIN2 ==> E_COVERSIN_2 E_COVERSIN3 ==> E_COVERSIN_3 E_ACOVERSIN1 ==> E_ACOVERSIN_1 E_ACOVERSIN2 ==> E_ACOVERSIN_2 E_ACOVERSIN3 ==> E_ACOVERSIN_3 E_VERCOS1 ==> E_VERCOS_1 E_VERCOS2 ==> E_VERCOS_2 E_VERCOS3 ==> E_VERCOS_3 E_AVERCOS1 ==> E_AVERCOS_1 E_AVERCOS2 ==> E_AVERCOS_2 E_AVERCOS3 ==> E_AVERCOS_3 E_COVERCOS1 ==> E_COVERCOS_1 E_COVERCOS2 ==> E_COVERCOS_2 E_COVERCOS3 ==> E_COVERCOS_3 E_ACOVERCOS1 ==> E_ACOVERCOS_1 E_ACOVERCOS2 ==> E_ACOVERCOS_2 E_ACOVERCOS3 ==> E_ACOVERCOS_3 E_TAN5 ==> E_TAN_5 E_COT5 ==> E_COT_5 E_COT6 ==> E_COT_6 E_SEC5 ==> E_SEC_5 E_CSC5 ==> E_CSC_5 E_CSC6 ==> E_CSC_6
2842 lines
58 KiB
C
2842 lines
58 KiB
C
/*
|
|
* codegen - module to generate opcodes from the input tokens
|
|
*
|
|
* Copyright (C) 1999-2007,2017,2021-2023 David I. Bell and Ernest Bowen
|
|
*
|
|
* Primary author: David I. Bell
|
|
*
|
|
* Calc is open software; you can redistribute it and/or modify it under
|
|
* the terms of the version 2.1 of the GNU Lesser General Public License
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* Calc is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
|
|
* Public License for more details.
|
|
*
|
|
* A copy of version 2.1 of the GNU Lesser General Public License is
|
|
* distributed with calc under the filename COPYING-LGPL. You should have
|
|
* received a copy with calc; if not, write to Free Software Foundation, Inc.
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Under source code control: 1990/02/15 01:48:13
|
|
* File existed as early as: before 1990
|
|
*
|
|
* Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include "have_unistd.h"
|
|
#if defined(HAVE_UNISTD_H)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "lib_calc.h"
|
|
#include "calc.h"
|
|
#include "alloc.h"
|
|
#include "token.h"
|
|
#include "symbol.h"
|
|
#include "label.h"
|
|
#include "opcodes.h"
|
|
#include "str.h"
|
|
#include "func.h"
|
|
#include "conf.h"
|
|
#include "strl.h"
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
#if !defined(__CYGWIN__)
|
|
# include <direct.h>
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#include "errtbl.h"
|
|
#include "banned.h" /* include after system header <> includes */
|
|
|
|
|
|
STATIC bool rdonce; /* true => do not reread this file */
|
|
|
|
FUNC *curfunc;
|
|
|
|
S_FUNC int getsymvalue(char *name, VALUE *v_p);
|
|
S_FUNC int getfilename(char *name, size_t namelen, bool *once);
|
|
S_FUNC bool getid(char *buf);
|
|
S_FUNC void getshowstatement(void);
|
|
S_FUNC void getfunction(void);
|
|
S_FUNC void ungetfunction(void);
|
|
S_FUNC void getbody(LABEL *contlabel, LABEL *breaklabel,
|
|
LABEL *nextcaselabel, LABEL *defaultlabel);
|
|
S_FUNC int getdeclarations(int symtype);
|
|
S_FUNC int getsimpledeclaration (int symtype);
|
|
S_FUNC int getonevariable (int symtype);
|
|
S_FUNC void getstatement(LABEL *contlabel, LABEL *breaklabel,
|
|
LABEL *nextcaselabel, LABEL *defaultlabel);
|
|
S_FUNC void getobjdeclaration(int symtype);
|
|
S_FUNC void getoneobj(long index, int symtype);
|
|
S_FUNC void getobjvars(char *name, int symtype);
|
|
S_FUNC void getmatdeclaration(int symtype);
|
|
S_FUNC void getonematrix(int symtype);
|
|
S_FUNC void creatematrix(void);
|
|
S_FUNC void getsimplebody(void);
|
|
S_FUNC void getcondition(void);
|
|
S_FUNC void getmatargs(void);
|
|
S_FUNC void getelement(void);
|
|
S_FUNC void usesymbol(char *name, int autodef);
|
|
S_FUNC void definesymbol(char *name, int symtype);
|
|
S_FUNC void getcallargs(char *name);
|
|
S_FUNC void do_changedir(void);
|
|
S_FUNC int getexprlist(void);
|
|
S_FUNC int getopassignment(void);
|
|
S_FUNC int getassignment(void);
|
|
S_FUNC int getaltcond(void);
|
|
S_FUNC int getorcond(void);
|
|
S_FUNC int getandcond(void);
|
|
S_FUNC int getrelation(void);
|
|
S_FUNC int getsum(void);
|
|
S_FUNC int getproduct(void);
|
|
S_FUNC int getorexpr(void);
|
|
S_FUNC int getandexpr(void);
|
|
S_FUNC int getshiftexpr(void);
|
|
S_FUNC int getreference(void);
|
|
S_FUNC int getincdecexpr(void);
|
|
S_FUNC int getterm(void);
|
|
S_FUNC int getidexpr(bool okmat, int autodef);
|
|
S_FUNC long getinitlist(void);
|
|
|
|
#define INDICALLOC 8
|
|
|
|
STATIC int quickindices[INDICALLOC];
|
|
STATIC int * newindices;
|
|
STATIC int * indices;
|
|
STATIC int maxindices;
|
|
|
|
|
|
/*
|
|
* Read all the commands from an input file.
|
|
* These are either declarations, or else are commands to execute now.
|
|
* In general, commands are terminated by newlines or semicolons.
|
|
* Exceptions are function definitions and escaped newlines.
|
|
* Commands are read and executed until the end of file.
|
|
* The toplevel flag indicates whether we are at the top interactive level.
|
|
*/
|
|
void
|
|
getcommands(bool toplevel)
|
|
{
|
|
char name[MAXCMD+1+1]; /* program name */
|
|
|
|
/* firewall */
|
|
name[0] = '\0';
|
|
name[MAXCMD+1] = '\0';
|
|
abort_now = false;
|
|
|
|
/* getcommands */
|
|
if (!toplevel)
|
|
enterfilescope();
|
|
for (;;) {
|
|
int i;
|
|
(void) tokenmode(TM_NEWLINES);
|
|
switch (gettoken()) {
|
|
|
|
case T_DEFINE:
|
|
getfunction();
|
|
break;
|
|
|
|
case T_EOF:
|
|
if (!toplevel)
|
|
exitfilescope();
|
|
return;
|
|
|
|
case T_HELP:
|
|
for (i=1;;i++) {
|
|
switch(getfilename(name, MAXCMD+1, NULL)) {
|
|
case 1:
|
|
case -1:
|
|
if(i == 1) {
|
|
strlcpy(name,
|
|
DEFAULTCALCHELP,
|
|
MAXCMD+1);
|
|
givehelp(name);
|
|
}
|
|
break;
|
|
case 0:
|
|
givehelp(name);
|
|
continue;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case T_READ:
|
|
if (!allow_read) {
|
|
scanerror(T_NULL,
|
|
"read command disallowed by -m mode\n");
|
|
break;
|
|
}
|
|
for (;;) {
|
|
int open_ret;
|
|
|
|
if (getfilename(name, MAXCMD+1, &rdonce))
|
|
break;
|
|
open_ret = opensearchfile(name,calcpath,
|
|
CALCEXT,rdonce);
|
|
switch (open_ret) {
|
|
case 0:
|
|
getcommands(false);
|
|
closeinput();
|
|
continue;
|
|
case 1:
|
|
/* prev read and -once was given */
|
|
continue;
|
|
case -2:
|
|
scanerror(T_NULL,
|
|
"Maximum input depth reached");
|
|
break;
|
|
default:
|
|
scanerror(T_NULL,
|
|
"Cannot open \"%s\"", name);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case T_WRITE:
|
|
if (!allow_write) {
|
|
scanerror(T_NULL,
|
|
"write command disallowed by -m mode\n");
|
|
break;
|
|
}
|
|
if (getfilename(name, MAXCMD+1, NULL))
|
|
break;
|
|
if (writeglobals(name)) {
|
|
scanerror(T_NULL,
|
|
"Error writing \"%s\"\n", name);
|
|
}
|
|
break;
|
|
|
|
case T_CD:
|
|
do_changedir();
|
|
break;
|
|
case T_NEWLINE:
|
|
case T_SEMICOLON:
|
|
break;
|
|
|
|
default:
|
|
rescantoken();
|
|
initstack();
|
|
if (evaluate(false))
|
|
updateoldvalue(curfunc);
|
|
freefunc(curfunc);
|
|
if (abort_now) {
|
|
if (!stdin_tty)
|
|
run_state = RUN_EXIT;
|
|
else if (run_state < RUN_PRE_TOP_LEVEL)
|
|
run_state = RUN_PRE_TOP_LEVEL;
|
|
if (calc_use_scanerr_jmpbuf != 0) {
|
|
longjmp(calc_scanerr_jmpbuf, 30);
|
|
} else {
|
|
fprintf(stderr,
|
|
"calc_scanerr_jmpbuf not setup, exiting code 30\n");
|
|
libcalc_call_me_last();
|
|
exit(30);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Evaluate a line of statements.
|
|
* This is done by treating the current line as a function body,
|
|
* compiling it, and then executing it. Returns true if the line
|
|
* successfully compiled and executed. The last expression result
|
|
* is saved in the f_savedvalue element of the current function.
|
|
* The nestflag variable should be false for the outermost evaluation
|
|
* level, and true for all other calls (such as the 'eval' function).
|
|
* The function name begins with an asterisk to indicate specialness.
|
|
*
|
|
* given:
|
|
* nestflag true if this is a nested evaluation
|
|
*/
|
|
bool
|
|
evaluate(bool nestflag)
|
|
{
|
|
char *funcname;
|
|
int loop = 1; /* 0 => end the main while loop */
|
|
|
|
funcname = (nestflag ? "**" : "*");
|
|
beginfunc(funcname, nestflag);
|
|
if (gettoken() == T_LEFTBRACE) {
|
|
getbody(NULL_LABEL, NULL_LABEL, NULL_LABEL, NULL_LABEL);
|
|
} else {
|
|
if (nestflag)
|
|
(void) tokenmode(TM_DEFAULT);
|
|
rescantoken();
|
|
while (loop) {
|
|
switch (gettoken()) {
|
|
case T_SEMICOLON:
|
|
break;
|
|
case T_NEWLINE:
|
|
case T_EOF:
|
|
loop = 0;
|
|
break;
|
|
|
|
default:
|
|
rescantoken();
|
|
getstatement(NULL_LABEL, NULL_LABEL,
|
|
NULL_LABEL, NULL_LABEL);
|
|
}
|
|
}
|
|
}
|
|
addop(OP_UNDEF);
|
|
addop(OP_RETURN);
|
|
checklabels();
|
|
if (errorcount)
|
|
return false;
|
|
calculate(curfunc, 0);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Undefine one or more functions
|
|
*/
|
|
S_FUNC void
|
|
ungetfunction(void)
|
|
{
|
|
char *name;
|
|
int type;
|
|
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_COMMA:
|
|
continue;
|
|
case T_SYMBOL:
|
|
name = tokensymbol();
|
|
type = getbuiltinfunc(name);
|
|
if (type >= 0) {
|
|
warning(
|
|
"Cannot undefine builtin function \"%s\"", name);
|
|
continue;
|
|
}
|
|
rmuserfunc(name);
|
|
continue;
|
|
case T_MULT:
|
|
rmalluserfunc();
|
|
continue;
|
|
case T_STATIC:
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_SEMICOLON,
|
|
"Non-identifier following \"undefine static\"");
|
|
return;
|
|
}
|
|
name = tokensymbol();
|
|
endscope(name, false);
|
|
continue;
|
|
|
|
case T_NEWLINE:
|
|
case T_SEMICOLON:
|
|
case T_EOF:
|
|
rescantoken();
|
|
return;
|
|
default:
|
|
scanerror(T_SEMICOLON, "Non-name arg for undefine");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a function declaration.
|
|
* func = name '(' '' | name [ ',' name] ... ')' simplebody
|
|
* | name '(' '' | name [ ',' name] ... ')' body.
|
|
*/
|
|
S_FUNC void
|
|
getfunction(void)
|
|
{
|
|
char *name; /* parameter name */
|
|
int type; /* type of token read */
|
|
LABEL label;
|
|
long index;
|
|
|
|
(void) tokenmode(TM_DEFAULT);
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_NULL, "Function name was expected");
|
|
return;
|
|
}
|
|
name = tokensymbol();
|
|
type = getbuiltinfunc(name);
|
|
if (type >= 0) {
|
|
scanerror(T_SEMICOLON, "Using builtin function name");
|
|
return;
|
|
}
|
|
beginfunc(name, false);
|
|
enterfuncscope();
|
|
if (gettoken() != T_LEFTPAREN) {
|
|
scanerror(T_SEMICOLON,
|
|
"Left parenthesis expected for function");
|
|
return;
|
|
}
|
|
index = 0;
|
|
for (;;) {
|
|
type = gettoken();
|
|
if (type == T_RIGHTPAREN)
|
|
break;
|
|
if (type != T_SYMBOL) {
|
|
scanerror(T_COMMA,
|
|
"Using non-identifier as function parameter");
|
|
return;
|
|
}
|
|
name = tokensymbol();
|
|
switch (symboltype(name)) {
|
|
case SYM_UNDEFINED:
|
|
case SYM_GLOBAL:
|
|
case SYM_STATIC:
|
|
index = addparam(name);
|
|
break;
|
|
default:
|
|
scanerror(T_NULL,
|
|
"Parameter \"%s\" is already defined",
|
|
name);
|
|
}
|
|
type = gettoken();
|
|
if (type == T_ASSIGN) {
|
|
clearlabel(&label);
|
|
addopone(OP_PARAMADDR, index);
|
|
addoplabel(OP_JUMPNN, &label);
|
|
getopassignment();
|
|
addop(OP_ASSIGNPOP);
|
|
setlabel(&label);
|
|
type = gettoken();
|
|
}
|
|
|
|
if (type == T_RIGHTPAREN)
|
|
break;
|
|
if (type != T_COMMA) {
|
|
scanerror(T_COMMA,
|
|
"Using other than comma to separate parameters");
|
|
return;
|
|
}
|
|
}
|
|
switch (gettoken()) {
|
|
case T_ASSIGN:
|
|
getsimplebody();
|
|
break;
|
|
case T_LEFTBRACE:
|
|
getbody(NULL_LABEL, NULL_LABEL, NULL_LABEL,
|
|
NULL_LABEL);
|
|
break;
|
|
default:
|
|
scanerror(T_NULL,
|
|
"Left brace or equals sign expected for function");
|
|
return;
|
|
}
|
|
endfunc();
|
|
exitfuncscope();
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a simple assignment style body for a function declaration.
|
|
* simplebody = '=' assignment '\n'.
|
|
*/
|
|
S_FUNC void
|
|
getsimplebody(void)
|
|
{
|
|
(void) tokenmode(TM_NEWLINES);
|
|
(void) getexprlist();
|
|
addop(OP_RETURN);
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the body of a function, or a sub-body of a function.
|
|
* body = '{' [ declarations ] ... [ statement ] ... '}'
|
|
* | [ declarations ] ... [statement ] ... '\n'
|
|
*/
|
|
/*ARGSUSED*/
|
|
S_FUNC void
|
|
getbody(LABEL *contlabel, LABEL *breaklabel, LABEL *nextcaselabel,
|
|
LABEL *defaultlabel)
|
|
{
|
|
int oldmode;
|
|
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
while (true) {
|
|
switch (gettoken()) {
|
|
case T_RIGHTBRACE:
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
|
|
case T_EOF:
|
|
scanerror(T_NULL, "End-of-file in function body");
|
|
return;
|
|
|
|
default:
|
|
rescantoken();
|
|
getstatement(contlabel, breaklabel,
|
|
nextcaselabel, defaultlabel);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a line of possible local, global, or static variable declarations.
|
|
* declarations = { LOCAL | GLOBAL | STATIC } onedeclaration
|
|
* [ ',' onedeclaration ] ... ';'.
|
|
*/
|
|
S_FUNC int
|
|
getdeclarations(int symtype)
|
|
{
|
|
int res = 0;
|
|
|
|
while (true) {
|
|
switch (gettoken()) {
|
|
case T_COMMA:
|
|
continue;
|
|
|
|
case T_NEWLINE:
|
|
case T_SEMICOLON:
|
|
case T_RIGHTBRACE:
|
|
case T_EOF:
|
|
rescantoken();
|
|
return res;
|
|
|
|
case T_SYMBOL:
|
|
addopone(OP_DEBUG, linenumber());
|
|
rescantoken();
|
|
if (getsimpledeclaration(symtype))
|
|
res = 1;
|
|
break;
|
|
|
|
case T_MAT:
|
|
addopone(OP_DEBUG, linenumber());
|
|
getmatdeclaration(symtype);
|
|
res = 1;
|
|
break;
|
|
|
|
case T_OBJ:
|
|
addopone(OP_DEBUG, linenumber());
|
|
getobjdeclaration(symtype);
|
|
addop(OP_POP);
|
|
res = 1;
|
|
break;
|
|
|
|
default:
|
|
scanerror(T_SEMICOLON,
|
|
"Bad syntax in declaration statement");
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get declaration of a sequence of simple identifiers, as in
|
|
* global a, b = 1, c d = 2, d;
|
|
* Subsequences end with "," or at end of line; spaces indicate
|
|
* repeated assignment, e.g. "c d = 2" has the effect of "c = 2, d = 2".
|
|
*/
|
|
S_FUNC int
|
|
getsimpledeclaration(int symtype)
|
|
{
|
|
int res = 0;
|
|
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_SYMBOL:
|
|
rescantoken();
|
|
if (getonevariable(symtype)) {
|
|
res = 1;
|
|
addop(OP_POP);
|
|
}
|
|
continue;
|
|
case T_COMMA:
|
|
continue;
|
|
default:
|
|
rescantoken();
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get one variable in a sequence of simple identifiers.
|
|
* Returns 1 if the subsequence in which the variable occurs ends with
|
|
* an assignment, e.g. for the variables b, c, d, in
|
|
* S_FUNC a, b = 1, c d = 2, d;
|
|
*/
|
|
S_FUNC int
|
|
getonevariable(int symtype)
|
|
{
|
|
char *name;
|
|
int res = 0;
|
|
|
|
switch(gettoken()) {
|
|
case T_SYMBOL:
|
|
name = addliteral(tokensymbol());
|
|
res = getonevariable(symtype);
|
|
definesymbol(name, symtype);
|
|
if (res) {
|
|
usesymbol(name, 0);
|
|
addop(OP_ASSIGNBACK);
|
|
}
|
|
return res;
|
|
case T_ASSIGN:
|
|
getopassignment();
|
|
rescantoken();
|
|
return 1;
|
|
default:
|
|
rescantoken();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get a statement.
|
|
* statement = IF condition statement [ELSE statement]
|
|
* | FOR '(' [assignment] ';' [assignment] ';' [assignment] ')' statement
|
|
* | WHILE condition statement
|
|
* | DO statement WHILE condition ';'
|
|
* | SWITCH condition '{' [caseclause] ... '}'
|
|
* | CONTINUE ';'
|
|
* | BREAK ';'
|
|
* | RETURN assignment ';'
|
|
* | GOTO label ';'
|
|
* | PRINT assignment [, assignment ] ... ';'
|
|
* | QUIT [ string ] ';'
|
|
* | ABORT [ string ] ';'
|
|
* | SHOW item ';'
|
|
* | body
|
|
* | assignment ';'
|
|
* | label ':' statement
|
|
* | ';'.
|
|
*
|
|
* given:
|
|
* contlabel label for continue statement
|
|
* breaklabel label for break statement
|
|
* nextcaselabel label for next case statement
|
|
* defaultlabel label for default case
|
|
*/
|
|
S_FUNC void
|
|
getstatement(LABEL *contlabel, LABEL *breaklabel,
|
|
LABEL *nextcaselabel, LABEL *defaultlabel)
|
|
{
|
|
LABEL label;
|
|
LABEL label1, label2, label3, label4; /* locations for jumps */
|
|
int type;
|
|
bool printeol;
|
|
int oldmode;
|
|
|
|
addopone(OP_DEBUG, linenumber());
|
|
switch (gettoken()) {
|
|
case T_NEWLINE:
|
|
case T_SEMICOLON:
|
|
return;
|
|
|
|
case T_GLOBAL:
|
|
(void) getdeclarations(SYM_GLOBAL);
|
|
break;
|
|
|
|
case T_STATIC:
|
|
clearlabel(&label);
|
|
addoplabel(OP_INITSTATIC, &label);
|
|
if (getdeclarations(SYM_STATIC))
|
|
setlabel(&label);
|
|
else
|
|
curfunc->f_opcodecount -= 2;
|
|
break;
|
|
|
|
case T_LOCAL:
|
|
(void) getdeclarations(SYM_LOCAL);
|
|
break;
|
|
|
|
case T_UNDEFINE:
|
|
ungetfunction();
|
|
break;
|
|
|
|
case T_RIGHTBRACE:
|
|
scanerror(T_NULL, "Extraneous right brace");
|
|
return;
|
|
|
|
case T_CONTINUE:
|
|
if (contlabel == NULL_LABEL) {
|
|
scanerror(T_SEMICOLON,
|
|
"CONTINUE not within FOR, WHILE, or DO");
|
|
return;
|
|
}
|
|
addoplabel(OP_JUMP, contlabel);
|
|
break;
|
|
|
|
case T_BREAK:
|
|
if (breaklabel == NULL_LABEL) {
|
|
scanerror(T_SEMICOLON,
|
|
"BREAK not within FOR, WHILE, or DO");
|
|
return;
|
|
}
|
|
addoplabel(OP_JUMP, breaklabel);
|
|
break;
|
|
|
|
case T_GOTO:
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_SEMICOLON, "Missing label in goto");
|
|
return;
|
|
}
|
|
addop(OP_JUMP);
|
|
addlabel(tokensymbol());
|
|
break;
|
|
|
|
case T_RETURN:
|
|
switch (gettoken()) {
|
|
case T_NEWLINE:
|
|
case T_SEMICOLON:
|
|
addop(OP_UNDEF);
|
|
addop(OP_RETURN);
|
|
return;
|
|
default:
|
|
rescantoken();
|
|
(void) getexprlist();
|
|
if (curfunc->f_name[0] == '*')
|
|
addop(OP_SAVE);
|
|
addop(OP_RETURN);
|
|
}
|
|
break;
|
|
|
|
case T_LEFTBRACE:
|
|
getbody(contlabel, breaklabel, nextcaselabel, defaultlabel);
|
|
return;
|
|
|
|
case T_IF:
|
|
clearlabel(&label1);
|
|
clearlabel(&label2);
|
|
getcondition();
|
|
switch(gettoken()) {
|
|
case T_CONTINUE:
|
|
if (contlabel == NULL_LABEL) {
|
|
scanerror(T_SEMICOLON,
|
|
"CONTINUE not within FOR, "
|
|
"WHILE, or DO");
|
|
return;
|
|
}
|
|
addoplabel(OP_JUMPNZ, contlabel);
|
|
break;
|
|
case T_BREAK:
|
|
if (breaklabel == NULL_LABEL) {
|
|
scanerror(T_SEMICOLON,
|
|
"BREAK not within FOR, "
|
|
"WHILE, or DO");
|
|
return;
|
|
}
|
|
addoplabel(OP_JUMPNZ, breaklabel);
|
|
break;
|
|
case T_GOTO:
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_SEMICOLON,
|
|
"Missing label in goto");
|
|
return;
|
|
}
|
|
addop(OP_JUMPNZ);
|
|
addlabel(tokensymbol());
|
|
break;
|
|
default:
|
|
addoplabel(OP_JUMPZ, &label1);
|
|
rescantoken();
|
|
getstatement(contlabel, breaklabel,
|
|
NULL_LABEL, NULL_LABEL);
|
|
if (gettoken() != T_ELSE) {
|
|
setlabel(&label1);
|
|
rescantoken();
|
|
return;
|
|
}
|
|
addoplabel(OP_JUMP, &label2);
|
|
setlabel(&label1);
|
|
getstatement(contlabel, breaklabel,
|
|
NULL_LABEL, NULL_LABEL);
|
|
setlabel(&label2);
|
|
return;
|
|
}
|
|
if (gettoken() != T_SEMICOLON) /* This makes ';' optional */
|
|
rescantoken();
|
|
if (gettoken() != T_ELSE) {
|
|
rescantoken();
|
|
return;
|
|
}
|
|
getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL);
|
|
return;
|
|
|
|
case T_FOR: /* for (a; b; c) x */
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
clearlabel(&label1);
|
|
clearlabel(&label2);
|
|
clearlabel(&label3);
|
|
clearlabel(&label4);
|
|
contlabel = NULL_LABEL;
|
|
breaklabel = &label4;
|
|
if (gettoken() != T_LEFTPAREN) {
|
|
(void) tokenmode(oldmode);
|
|
scanerror(T_SEMICOLON, "Left parenthesis expected");
|
|
return;
|
|
}
|
|
if (gettoken() != T_SEMICOLON) { /* have 'a' part */
|
|
rescantoken();
|
|
(void) getexprlist();
|
|
addop(OP_POP);
|
|
if (gettoken() != T_SEMICOLON) {
|
|
(void) tokenmode(oldmode);
|
|
scanerror(T_SEMICOLON, "Missing semicolon");
|
|
return;
|
|
}
|
|
}
|
|
if (gettoken() != T_SEMICOLON) { /* have 'b' part */
|
|
setlabel(&label1);
|
|
contlabel = &label1;
|
|
rescantoken();
|
|
(void) getexprlist();
|
|
addoplabel(OP_JUMPNZ, &label3);
|
|
addoplabel(OP_JUMP, breaklabel);
|
|
if (gettoken() != T_SEMICOLON) {
|
|
(void) tokenmode(oldmode);
|
|
scanerror(T_SEMICOLON, "Missing semicolon");
|
|
return;
|
|
}
|
|
}
|
|
if (gettoken() != T_RIGHTPAREN) { /* have 'c' part */
|
|
if (label1.l_offset < 0)
|
|
addoplabel(OP_JUMP, &label3);
|
|
setlabel(&label2);
|
|
contlabel = &label2;
|
|
rescantoken();
|
|
(void) getexprlist();
|
|
addop(OP_POP);
|
|
if (label1.l_offset >= 0)
|
|
addoplabel(OP_JUMP, &label1);
|
|
if (gettoken() != T_RIGHTPAREN) {
|
|
(void) tokenmode(oldmode);
|
|
scanerror(T_SEMICOLON,
|
|
"Right parenthesis expected");
|
|
return;
|
|
}
|
|
}
|
|
setlabel(&label3);
|
|
if (contlabel == NULL_LABEL)
|
|
contlabel = &label3;
|
|
(void) tokenmode(oldmode);
|
|
getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL);
|
|
addoplabel(OP_JUMP, contlabel);
|
|
setlabel(breaklabel);
|
|
return;
|
|
|
|
case T_WHILE:
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
contlabel = &label1;
|
|
clearlabel(contlabel);
|
|
setlabel(contlabel);
|
|
getcondition();
|
|
(void) tokenmode(oldmode);
|
|
if (gettoken() != T_SEMICOLON) {
|
|
breaklabel = &label2;
|
|
clearlabel(breaklabel);
|
|
addoplabel(OP_JUMPZ, breaklabel);
|
|
rescantoken();
|
|
getstatement(contlabel, breaklabel,
|
|
NULL_LABEL, NULL_LABEL);
|
|
addoplabel(OP_JUMP, contlabel);
|
|
setlabel(breaklabel);
|
|
} else {
|
|
addoplabel(OP_JUMPNZ, contlabel);
|
|
}
|
|
return;
|
|
|
|
case T_DO:
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
contlabel = &label1;
|
|
breaklabel = &label2;
|
|
clearlabel(contlabel);
|
|
clearlabel(breaklabel);
|
|
clearlabel(&label3);
|
|
setlabel(&label3);
|
|
getstatement(contlabel, breaklabel, NULL_LABEL, NULL_LABEL);
|
|
if (gettoken() != T_WHILE) {
|
|
(void) tokenmode(oldmode);
|
|
scanerror(T_SEMICOLON,
|
|
"WHILE keyword expected for DO statement");
|
|
return;
|
|
}
|
|
setlabel(contlabel);
|
|
getcondition();
|
|
addoplabel(OP_JUMPNZ, &label3);
|
|
setlabel(breaklabel);
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
|
|
case T_SWITCH:
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
breaklabel = &label1;
|
|
nextcaselabel = &label2;
|
|
defaultlabel = &label3;
|
|
clearlabel(breaklabel);
|
|
clearlabel(nextcaselabel);
|
|
clearlabel(defaultlabel);
|
|
getcondition();
|
|
if (gettoken() != T_LEFTBRACE) {
|
|
(void) tokenmode(oldmode);
|
|
scanerror(T_SEMICOLON,
|
|
"Missing left brace for switch statement");
|
|
return;
|
|
}
|
|
addoplabel(OP_JUMP, nextcaselabel);
|
|
rescantoken();
|
|
getstatement(contlabel, breaklabel,
|
|
nextcaselabel, defaultlabel);
|
|
addoplabel(OP_JUMP, breaklabel);
|
|
setlabel(nextcaselabel);
|
|
if (defaultlabel->l_offset > 0)
|
|
addoplabel(OP_JUMP, defaultlabel);
|
|
else
|
|
addop(OP_POP);
|
|
setlabel(breaklabel);
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
|
|
case T_CASE:
|
|
if (nextcaselabel == NULL_LABEL) {
|
|
scanerror(T_SEMICOLON,
|
|
"CASE not within SWITCH statement");
|
|
return;
|
|
}
|
|
clearlabel(&label1);
|
|
addoplabel(OP_JUMP, &label1);
|
|
setlabel(nextcaselabel);
|
|
clearlabel(nextcaselabel);
|
|
(void) getexprlist();
|
|
if (gettoken() != T_COLON) {
|
|
scanerror(T_SEMICOLON,
|
|
"Colon expected after CASE expression");
|
|
return;
|
|
}
|
|
addoplabel(OP_CASEJUMP, nextcaselabel);
|
|
setlabel(&label1);
|
|
getstatement(contlabel, breaklabel,
|
|
nextcaselabel, defaultlabel);
|
|
return;
|
|
|
|
case T_DEFAULT:
|
|
if (gettoken() != T_COLON) {
|
|
scanerror(T_SEMICOLON,
|
|
"Colon expected after DEFAULT keyword");
|
|
return;
|
|
}
|
|
if (defaultlabel == NULL_LABEL) {
|
|
scanerror(T_SEMICOLON,
|
|
"DEFAULT not within SWITCH statement");
|
|
return;
|
|
}
|
|
if (defaultlabel->l_offset > 0) {
|
|
scanerror(T_SEMICOLON,
|
|
"Multiple DEFAULT clauses in SWITCH");
|
|
return;
|
|
}
|
|
clearlabel(&label1);
|
|
addoplabel(OP_JUMP, &label1);
|
|
setlabel(defaultlabel);
|
|
addop(OP_POP);
|
|
setlabel(&label1);
|
|
getstatement(contlabel, breaklabel,
|
|
nextcaselabel, defaultlabel);
|
|
return;
|
|
|
|
case T_ELSE:
|
|
scanerror(T_SEMICOLON, "ELSE without preceding IF");
|
|
return;
|
|
|
|
case T_SHOW:
|
|
getshowstatement();
|
|
break;
|
|
|
|
case T_PRINT:
|
|
printeol = true;
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_RIGHTPAREN:
|
|
case T_RIGHTBRACKET:
|
|
case T_RIGHTBRACE:
|
|
case T_NEWLINE:
|
|
case T_ELSE:
|
|
case T_EOF:
|
|
rescantoken();
|
|
/*FALLTHRU*/
|
|
case T_SEMICOLON:
|
|
if (printeol)
|
|
addop(OP_PRINTEOL);
|
|
return;
|
|
case T_COMMA:
|
|
addop(OP_PRINTSPACE);
|
|
/*FALLTHRU*/
|
|
case T_COLON:
|
|
printeol = false;
|
|
break;
|
|
case T_STRING:
|
|
printeol = true;
|
|
addopone(OP_PRINTSTRING, tokenstring());
|
|
break;
|
|
default:
|
|
printeol = true;
|
|
rescantoken();
|
|
(void) getopassignment();
|
|
addopone(OP_PRINT, (long) PRINT_NORMAL);
|
|
}
|
|
}
|
|
|
|
case T_QUIT:
|
|
switch (gettoken()) {
|
|
case T_STRING:
|
|
addopone(OP_QUIT, tokenstring());
|
|
break;
|
|
default:
|
|
addopone(OP_QUIT, -1);
|
|
rescantoken();
|
|
}
|
|
break;
|
|
|
|
case T_ABORT:
|
|
switch (gettoken()) {
|
|
case T_STRING:
|
|
addopone(OP_ABORT, tokenstring());
|
|
break;
|
|
default:
|
|
addopone(OP_ABORT, -1);
|
|
rescantoken();
|
|
}
|
|
break;
|
|
|
|
case T_SYMBOL:
|
|
if (nextchar() == ':') { /****HACK HACK****/
|
|
definelabel(tokensymbol());
|
|
if (gettoken() == T_RIGHTBRACE) {
|
|
rescantoken();
|
|
return;
|
|
}
|
|
rescantoken();
|
|
getstatement(contlabel, breaklabel,
|
|
NULL_LABEL, NULL_LABEL);
|
|
return;
|
|
}
|
|
reread();
|
|
/*FALLTHRU*/
|
|
|
|
default:
|
|
rescantoken();
|
|
type = getexprlist();
|
|
if (contlabel || breaklabel || (curfunc->f_name[0] != '*')) {
|
|
addop(OP_POP);
|
|
break;
|
|
}
|
|
addop(OP_SAVE);
|
|
if (isassign(type) || (curfunc->f_name[1] != '\0')) {
|
|
addop(OP_POP);
|
|
break;
|
|
}
|
|
addop(OP_PRINTRESULT);
|
|
break;
|
|
}
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_RIGHTBRACE:
|
|
case T_NEWLINE:
|
|
case T_EOF:
|
|
case T_ELSE:
|
|
rescantoken();
|
|
return;
|
|
case T_SEMICOLON:
|
|
return;
|
|
case T_NUMBER:
|
|
case T_IMAGINARY:
|
|
addopone(OP_NUMBER, tokennumber());
|
|
scanerror(T_NULL, "Unexpected number");
|
|
continue;
|
|
default:
|
|
scanerror(T_NULL, "Semicolon expected");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Read in an object declaration.
|
|
* This is of the following form:
|
|
* OBJ type [ '{' id [ ',' id ] ... '}' ] [ objlist ].
|
|
* The OBJ keyword has already been read. Symtype is SYM_UNDEFINED if this
|
|
* is an OBJ statement, otherwise this is part of a declaration which will
|
|
* define new symbols with the specified type.
|
|
*/
|
|
S_FUNC void
|
|
getobjdeclaration(int symtype)
|
|
{
|
|
char *name; /* name of object type */
|
|
int count; /* number of elements */
|
|
int index; /* current index */
|
|
int i; /* loop counter */
|
|
int oldmode;
|
|
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_SEMICOLON, "Object type name missing");
|
|
return;
|
|
}
|
|
name = addliteral(tokensymbol());
|
|
if (gettoken() != T_LEFTBRACE) {
|
|
rescantoken();
|
|
getobjvars(name, symtype);
|
|
return;
|
|
}
|
|
/*
|
|
* Read in the definition of the elements of the object.
|
|
*/
|
|
count = 0;
|
|
indices = quickindices;
|
|
maxindices = INDICALLOC;
|
|
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_SYMBOL:
|
|
if (count == maxindices) {
|
|
if (maxindices == INDICALLOC) {
|
|
maxindices += INDICALLOC;
|
|
newindices = (int *) malloc(maxindices *
|
|
sizeof(int));
|
|
if (newindices == NULL) {
|
|
scanerror(T_SEMICOLON,
|
|
"Out of memory for indices malloc");
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
}
|
|
memcpy(newindices, quickindices,
|
|
INDICALLOC * sizeof(int));
|
|
indices = newindices;
|
|
} else {
|
|
maxindices += INDICALLOC;
|
|
newindices = (int *) realloc(indices,
|
|
maxindices * sizeof(int));
|
|
if (newindices == NULL) {
|
|
free(indices);
|
|
scanerror(T_SEMICOLON,
|
|
"Out of memory for indices realloc");
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
}
|
|
indices = newindices;
|
|
}
|
|
}
|
|
index = addelement(tokensymbol());
|
|
for (i = 0; i < count; i++) {
|
|
if (indices[i] == index) {
|
|
if (indices != quickindices)
|
|
free(indices);
|
|
scanerror(T_SEMICOLON,
|
|
"Duplicate element name \"%s\"",
|
|
tokensymbol());
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
}
|
|
}
|
|
indices[count++] = index;
|
|
if (gettoken() == T_COMMA)
|
|
continue;
|
|
rescantoken();
|
|
if (gettoken() != T_RIGHTBRACE) {
|
|
if (indices != quickindices)
|
|
free(indices);
|
|
scanerror(T_SEMICOLON,
|
|
"Bad object type definition");
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
}
|
|
/*FALLTHRU*/
|
|
case T_RIGHTBRACE:
|
|
(void) tokenmode(oldmode);
|
|
if (defineobject(name, indices, count)) {
|
|
if (indices != quickindices)
|
|
free(indices);
|
|
scanerror(T_NULL,
|
|
"Object type \"%s\" is already defined", name);
|
|
return;
|
|
}
|
|
if (indices != quickindices)
|
|
free(indices);
|
|
getobjvars(name, symtype);
|
|
return;
|
|
case T_NEWLINE:
|
|
continue;
|
|
default:
|
|
if (indices != quickindices)
|
|
free(indices);
|
|
scanerror(T_SEMICOLON, "Bad object type definition");
|
|
(void) tokenmode(oldmode);
|
|
return;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
S_FUNC void
|
|
getoneobj(long index, int symtype)
|
|
{
|
|
char *symname;
|
|
|
|
if (gettoken() == T_SYMBOL) {
|
|
if (symtype == SYM_UNDEFINED) {
|
|
rescantoken();
|
|
(void) getidexpr(true, 1);
|
|
} else {
|
|
symname = tokensymbol();
|
|
definesymbol(symname, symtype);
|
|
usesymbol(symname, 0);
|
|
}
|
|
getoneobj(index, symtype);
|
|
addop(OP_ASSIGN);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
addopone(OP_OBJCREATE, index);
|
|
while (gettoken() == T_ASSIGN)
|
|
(void) getinitlist();
|
|
rescantoken();
|
|
}
|
|
|
|
/*
|
|
* Routine to assign a specified object-type value to each of a set of
|
|
* variables in a "global", "local" or "S_FUNC" declaration, or, if
|
|
* symtype is SYM_UNDEFINED, to create one object value of the specified
|
|
* type.
|
|
*
|
|
* given:
|
|
* name object name
|
|
* symtype declaration type
|
|
*/
|
|
S_FUNC void
|
|
getobjvars(char *name, int symtype)
|
|
{
|
|
long index; /* index for object */
|
|
|
|
index = checkobject(name);
|
|
if (index < 0) {
|
|
scanerror(T_SEMICOLON,
|
|
"Object %s has not been defined yet", name);
|
|
return;
|
|
}
|
|
for (;;) {
|
|
getoneobj(index, symtype);
|
|
if (symtype == SYM_UNDEFINED)
|
|
return;
|
|
if (gettoken() != T_COMMA) {
|
|
rescantoken();
|
|
return;
|
|
}
|
|
addop(OP_POP);
|
|
}
|
|
}
|
|
|
|
|
|
S_FUNC void
|
|
getmatdeclaration(int symtype)
|
|
{
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_SYMBOL:
|
|
rescantoken();
|
|
getonematrix(symtype);
|
|
addop(OP_POP);
|
|
continue;
|
|
case T_COMMA:
|
|
continue;
|
|
default:
|
|
rescantoken();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
S_FUNC void
|
|
getonematrix(int symtype)
|
|
{
|
|
long dim;
|
|
long index;
|
|
long count;
|
|
unsigned long patchpc;
|
|
char *name;
|
|
|
|
if (gettoken() == T_SYMBOL) {
|
|
if (symtype == SYM_UNDEFINED) {
|
|
rescantoken();
|
|
(void) getidexpr(false, 1);
|
|
} else {
|
|
name = tokensymbol();
|
|
definesymbol(name, symtype);
|
|
usesymbol(name, 0);
|
|
}
|
|
while (gettoken() == T_COMMA);
|
|
rescantoken();
|
|
getonematrix(symtype);
|
|
addop(OP_ASSIGN);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
|
|
if (gettoken() == T_LEFTPAREN) {
|
|
if (isrvalue(getexprlist())) {
|
|
scanerror(T_SEMICOLON, "Lvalue expected");
|
|
return;
|
|
}
|
|
if (gettoken() != T_RIGHTPAREN) {
|
|
scanerror(T_SEMICOLON, "Missing right parenthesis");
|
|
return;
|
|
}
|
|
getonematrix(symtype);
|
|
addop(OP_ASSIGN);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
|
|
if (gettoken() != T_LEFTBRACKET) {
|
|
rescantoken();
|
|
scanerror(T_SEMICOLON, "Left-bracket expected");
|
|
return;
|
|
}
|
|
dim = 1;
|
|
|
|
/*
|
|
* If there are no bounds given for the matrix, then they must be
|
|
* implicitly defined by a list of initialization values. Put in
|
|
* a dummy number in the opcode stream for the bounds and remember
|
|
* its location. After we know how many values are in the list, we
|
|
* will patch the correct value back into the opcode.
|
|
*/
|
|
if (gettoken() == T_RIGHTBRACKET) {
|
|
if (gettoken() == T_ASSIGN) {
|
|
clearopt();
|
|
patchpc = curfunc->f_opcodecount + 1;
|
|
addopone(OP_NUMBER, (long) -1);
|
|
clearopt();
|
|
addop(OP_ZERO);
|
|
addopone(OP_MATCREATE, dim);
|
|
addop(OP_ZERO);
|
|
addop(OP_INITFILL);
|
|
count = 0;
|
|
count = getinitlist();
|
|
index = addqconstant(itoq(count));
|
|
if (index < 0)
|
|
math_error("Cannot allocate constant");
|
|
curfunc->f_opcodes[patchpc] = index;
|
|
return;
|
|
}
|
|
rescantoken();
|
|
addopone(OP_MATCREATE, 0);
|
|
if (gettoken() == T_LEFTBRACKET) {
|
|
creatematrix();
|
|
} else {
|
|
rescantoken();
|
|
addop(OP_ZERO);
|
|
}
|
|
addop(OP_INITFILL);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This isn't implicit, so we expect expressions for the bounds.
|
|
*/
|
|
rescantoken();
|
|
creatematrix();
|
|
while (gettoken() == T_ASSIGN)
|
|
(void) getinitlist();
|
|
rescantoken();
|
|
}
|
|
|
|
|
|
S_FUNC void
|
|
creatematrix(void)
|
|
{
|
|
long dim;
|
|
|
|
dim = 0;
|
|
|
|
for (;;) {
|
|
if (gettoken() == T_RIGHTBRACKET) {
|
|
addopone(OP_MATCREATE, dim);
|
|
if (gettoken() == T_LEFTBRACKET) {
|
|
creatematrix();
|
|
} else {
|
|
rescantoken();
|
|
addop(OP_ZERO);
|
|
}
|
|
addop(OP_INITFILL);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
if (++dim > MAXDIM) {
|
|
scanerror(T_SEMICOLON,
|
|
"Only %d dimensions allowed", MAXDIM);
|
|
return;
|
|
}
|
|
(void) getopassignment();
|
|
switch (gettoken()) {
|
|
case T_RIGHTBRACKET:
|
|
rescantoken();
|
|
/*FALLTHRU*/
|
|
case T_COMMA:
|
|
addop(OP_ONE);
|
|
addop(OP_SUB);
|
|
addop(OP_ZERO);
|
|
break;
|
|
case T_COLON:
|
|
(void) getopassignment();
|
|
switch(gettoken()) {
|
|
case T_RIGHTBRACKET:
|
|
rescantoken();
|
|
/*FALLTHRU*/
|
|
case T_COMMA:
|
|
continue;
|
|
}
|
|
/*FALLTHRU*/
|
|
default:
|
|
rescantoken();
|
|
scanerror(T_SEMICOLON,
|
|
"Illegal matrix definition");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an optional initialization list for a matrix or object definition.
|
|
* Returns the number of elements that are in the list, or -1 on parse error.
|
|
* initlist = { assignment [ , assignment ] ... }.
|
|
*/
|
|
S_FUNC long
|
|
getinitlist(void)
|
|
{
|
|
long index;
|
|
int oldmode;
|
|
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
|
|
if (gettoken() != T_LEFTBRACE) {
|
|
scanerror(T_SEMICOLON,
|
|
"Missing left brace for initialization list");
|
|
(void) tokenmode(oldmode);
|
|
return -1;
|
|
}
|
|
|
|
for (index = 0; ; index++) {
|
|
switch(gettoken()) {
|
|
case T_COMMA:
|
|
case T_NEWLINE:
|
|
continue;
|
|
case T_RIGHTBRACE:
|
|
(void) tokenmode(oldmode);
|
|
return index;
|
|
case T_LEFTBRACE:
|
|
rescantoken();
|
|
addop(OP_DUPLICATE);
|
|
addopone(OP_ELEMADDR, index);
|
|
(void) getinitlist();
|
|
break;
|
|
default:
|
|
rescantoken();
|
|
getopassignment();
|
|
}
|
|
addopone(OP_ELEMINIT, index);
|
|
switch (gettoken()) {
|
|
case T_COMMA:
|
|
case T_NEWLINE:
|
|
continue;
|
|
|
|
case T_RIGHTBRACE:
|
|
(void) tokenmode(oldmode);
|
|
return index;
|
|
|
|
default:
|
|
scanerror(T_SEMICOLON,
|
|
"Missing right brace for initialization list");
|
|
(void) tokenmode(oldmode);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a condition.
|
|
* condition = '(' assignment ')'.
|
|
*/
|
|
S_FUNC void
|
|
getcondition(void)
|
|
{
|
|
if (gettoken() != T_LEFTPAREN) {
|
|
scanerror(T_SEMICOLON,
|
|
"Missing left parenthesis for condition");
|
|
return;
|
|
}
|
|
(void) getexprlist();
|
|
if (gettoken() != T_RIGHTPAREN) {
|
|
scanerror(T_SEMICOLON,
|
|
"Missing right parenthesis for condition");
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an expression list consisting of one or more expressions,
|
|
* separated by commas. The value of the list is that of the final expression.
|
|
* This is the top level routine for parsing expressions.
|
|
* Returns flags describing the type of the last assignment or expression found.
|
|
* exprlist = assignment [ ',' assignment ] ...
|
|
*/
|
|
S_FUNC int
|
|
getexprlist(void)
|
|
{
|
|
int type;
|
|
|
|
type = getopassignment();
|
|
while (gettoken() == T_COMMA) {
|
|
addop(OP_POP);
|
|
type = getopassignment();
|
|
}
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an op-assignment or possibly just an assignment or expression.
|
|
* Returns flags describing the type of assignment or expression found.
|
|
* assignment = lvalue '=' assignment
|
|
* | lvalue '+=' assignment
|
|
* | lvalue '-=' assignment
|
|
* | lvalue '*=' assignment
|
|
* | lvalue '/=' assignment
|
|
* | lvalue '%=' assignment
|
|
* | lvalue '//=' assignment
|
|
* | lvalue '&=' assignment
|
|
* | lvalue '|=' assignment
|
|
* | lvalue '<<=' assignment
|
|
* | lvalue '>>=' assignment
|
|
* | lvalue '^=' assignment
|
|
* | lvalue '**=' assignment
|
|
* | orcond.
|
|
*/
|
|
S_FUNC int
|
|
getopassignment(void)
|
|
{
|
|
int type; /* type of expression */
|
|
long op; /* opcode to generate */
|
|
|
|
type = getassignment();
|
|
switch (gettoken()) {
|
|
case T_PLUSEQUALS: op = OP_ADD; break;
|
|
case T_MINUSEQUALS: op = OP_SUB; break;
|
|
case T_MULTEQUALS: op = OP_MUL; break;
|
|
case T_DIVEQUALS: op = OP_DIV; break;
|
|
case T_SLASHSLASHEQUALS: op = OP_QUO; break;
|
|
case T_MODEQUALS: op = OP_MOD; break;
|
|
case T_ANDEQUALS: op = OP_AND; break;
|
|
case T_OREQUALS: op = OP_OR; break;
|
|
case T_LSHIFTEQUALS: op = OP_LEFTSHIFT; break;
|
|
case T_RSHIFTEQUALS: op = OP_RIGHTSHIFT; break;
|
|
case T_POWEREQUALS: op = OP_POWER; break;
|
|
case T_HASHEQUALS: op = OP_HASHOP; break;
|
|
case T_TILDEEQUALS: op = OP_XOR; break;
|
|
case T_BACKSLASHEQUALS: op = OP_SETMINUS; break;
|
|
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
if (isrvalue(type)) {
|
|
scanerror(T_NULL, "Illegal assignment");
|
|
(void) getopassignment();
|
|
return (EXPR_RVALUE | EXPR_ASSIGN);
|
|
}
|
|
writeindexop();
|
|
for(;;) {
|
|
addop(OP_DUPLICATE);
|
|
if (gettoken() == T_LEFTBRACE) {
|
|
rescantoken();
|
|
addop(OP_DUPVALUE);
|
|
getinitlist();
|
|
while (gettoken() == T_ASSIGN)
|
|
getinitlist();
|
|
rescantoken();
|
|
} else {
|
|
rescantoken();
|
|
(void) getassignment();
|
|
}
|
|
addop(op);
|
|
addop(OP_ASSIGN);
|
|
switch (gettoken()) {
|
|
case T_PLUSEQUALS: op = OP_ADD; break;
|
|
case T_MINUSEQUALS: op = OP_SUB; break;
|
|
case T_MULTEQUALS: op = OP_MUL; break;
|
|
case T_DIVEQUALS: op = OP_DIV; break;
|
|
case T_SLASHSLASHEQUALS: op = OP_QUO; break;
|
|
case T_MODEQUALS: op = OP_MOD; break;
|
|
case T_ANDEQUALS: op = OP_AND; break;
|
|
case T_OREQUALS: op = OP_OR; break;
|
|
case T_LSHIFTEQUALS: op = OP_LEFTSHIFT; break;
|
|
case T_RSHIFTEQUALS: op = OP_RIGHTSHIFT; break;
|
|
case T_POWEREQUALS: op = OP_POWER; break;
|
|
case T_HASHEQUALS: op = OP_HASHOP; break;
|
|
case T_TILDEEQUALS: op = OP_XOR; break;
|
|
case T_BACKSLASHEQUALS: op = OP_SETMINUS; break;
|
|
|
|
default:
|
|
rescantoken();
|
|
return EXPR_ASSIGN;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an assignment (lvalue = ...) or possibly just an expression
|
|
*/
|
|
|
|
S_FUNC int
|
|
getassignment (void)
|
|
{
|
|
int type; /* type of expression */
|
|
|
|
switch(gettoken()) {
|
|
case T_COMMA:
|
|
case T_SEMICOLON:
|
|
case T_NEWLINE:
|
|
case T_RIGHTPAREN:
|
|
case T_RIGHTBRACKET:
|
|
case T_RIGHTBRACE:
|
|
case T_EOF:
|
|
addop(OP_UNDEF);
|
|
rescantoken();
|
|
return EXPR_RVALUE;
|
|
}
|
|
|
|
rescantoken();
|
|
|
|
type = getaltcond();
|
|
|
|
switch (gettoken()) {
|
|
case T_NUMBER:
|
|
case T_IMAGINARY:
|
|
addopone(OP_NUMBER, tokennumber());
|
|
type = (EXPR_RVALUE | EXPR_CONST);
|
|
/*FALLTHRU*/
|
|
case T_STRING:
|
|
case T_SYMBOL:
|
|
case T_OLDVALUE:
|
|
case T_LEFTPAREN:
|
|
case T_PLUSPLUS:
|
|
case T_MINUSMINUS:
|
|
case T_NOT:
|
|
scanerror(T_NULL, "Missing operator");
|
|
return type;
|
|
case T_ASSIGN:
|
|
break;
|
|
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
if (isrvalue(type)) {
|
|
scanerror(T_SEMICOLON, "Illegal assignment");
|
|
(void) getassignment();
|
|
return (EXPR_RVALUE | EXPR_ASSIGN);
|
|
}
|
|
writeindexop();
|
|
if (gettoken() == T_LEFTBRACE) {
|
|
rescantoken();
|
|
getinitlist();
|
|
while (gettoken() == T_ASSIGN)
|
|
getinitlist();
|
|
rescantoken();
|
|
return EXPR_ASSIGN;
|
|
}
|
|
rescantoken();
|
|
(void) getassignment();
|
|
addop(OP_ASSIGN);
|
|
return EXPR_ASSIGN;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a possible conditional result expression (question mark).
|
|
* Flags are returned indicating the type of expression found.
|
|
* altcond = orcond [ '?' orcond ':' altcond ].
|
|
*/
|
|
S_FUNC int
|
|
getaltcond(void)
|
|
{
|
|
int type; /* type of expression */
|
|
LABEL donelab; /* label for done */
|
|
LABEL altlab; /* label for alternate expression */
|
|
|
|
type = getorcond();
|
|
if (gettoken() != T_QUESTIONMARK) {
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
clearlabel(&donelab);
|
|
clearlabel(&altlab);
|
|
addoplabel(OP_JUMPZ, &altlab);
|
|
type = getaltcond();
|
|
if (gettoken() != T_COLON) {
|
|
scanerror(T_SEMICOLON,
|
|
"Missing colon for conditional expression");
|
|
return EXPR_RVALUE;
|
|
}
|
|
addoplabel(OP_JUMP, &donelab);
|
|
setlabel(&altlab);
|
|
type |= getaltcond();
|
|
setlabel(&donelab);
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a possible conditional or expression.
|
|
* Flags are returned indicating the type of expression found.
|
|
* orcond = andcond [ '||' andcond ] ...
|
|
*/
|
|
S_FUNC int
|
|
getorcond(void)
|
|
{
|
|
int type; /* type of expression */
|
|
LABEL donelab; /* label for done */
|
|
|
|
clearlabel(&donelab);
|
|
type = getandcond();
|
|
while (gettoken() == T_OROR) {
|
|
addoplabel(OP_CONDORJUMP, &donelab);
|
|
type |= getandcond();
|
|
}
|
|
rescantoken();
|
|
if (donelab.l_chain >= 0)
|
|
setlabel(&donelab);
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a possible conditional and expression.
|
|
* Flags are returned indicating the type of expression found.
|
|
* andcond = relation [ '&&' relation ] ...
|
|
*/
|
|
S_FUNC int
|
|
getandcond(void)
|
|
{
|
|
int type; /* type of expression */
|
|
LABEL donelab; /* label for done */
|
|
|
|
clearlabel(&donelab);
|
|
type = getrelation();
|
|
while (gettoken() == T_ANDAND) {
|
|
addoplabel(OP_CONDANDJUMP, &donelab);
|
|
type |= getrelation();
|
|
}
|
|
rescantoken();
|
|
if (donelab.l_chain >= 0)
|
|
setlabel(&donelab);
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a possible relation (equality or inequality), or just an expression.
|
|
* Flags are returned indicating the type of relation found.
|
|
* relation = sum '==' sum
|
|
* | sum '!=' sum
|
|
* | sum '<=' sum
|
|
* | sum '>=' sum
|
|
* | sum '<' sum
|
|
* | sum '>' sum
|
|
* | sum.
|
|
*/
|
|
S_FUNC int
|
|
getrelation(void)
|
|
{
|
|
int type; /* type of expression */
|
|
long op; /* opcode to generate */
|
|
|
|
type = getsum();
|
|
switch (gettoken()) {
|
|
case T_EQ: op = OP_EQ; break;
|
|
case T_NE: op = OP_NE; break;
|
|
case T_LT: op = OP_LT; break;
|
|
case T_GT: op = OP_GT; break;
|
|
case T_LE: op = OP_LE; break;
|
|
case T_GE: op = OP_GE; break;
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
if (islvalue(type))
|
|
addop(OP_GETVALUE);
|
|
(void) getsum();
|
|
addop(op);
|
|
return EXPR_RVALUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an expression made up of sums of products.
|
|
* Flags indicating the type of expression found are returned.
|
|
* sum = product [ {'+' | '-'} product ] ...
|
|
*/
|
|
S_FUNC int
|
|
getsum(void)
|
|
{
|
|
int type; /* type of expression found */
|
|
long op; /* opcode to generate */
|
|
|
|
type = EXPR_RVALUE;
|
|
switch(gettoken()) {
|
|
case T_PLUS:
|
|
(void) getproduct();
|
|
addop(OP_PLUS);
|
|
break;
|
|
case T_MINUS:
|
|
(void) getproduct();
|
|
addop(OP_NEGATE);
|
|
break;
|
|
default:
|
|
rescantoken();
|
|
type = getproduct();
|
|
}
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_PLUS: op = OP_ADD; break;
|
|
case T_MINUS: op = OP_SUB; break;
|
|
case T_HASH: op = OP_HASHOP; break;
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
if (islvalue(type))
|
|
addop(OP_GETVALUE);
|
|
(void) getproduct();
|
|
addop(op);
|
|
type = EXPR_RVALUE;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the product of arithmetic or expressions.
|
|
* Flags indicating the type of expression found are returned.
|
|
* product = orexpr [ {'*' | '/' | '//' | '%'} orexpr ] ...
|
|
*/
|
|
S_FUNC int
|
|
getproduct(void)
|
|
{
|
|
int type; /* type of value found */
|
|
long op; /* opcode to generate */
|
|
|
|
type = getorexpr();
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_MULT: op = OP_MUL; break;
|
|
case T_DIV: op = OP_DIV; break;
|
|
case T_MOD: op = OP_MOD; break;
|
|
case T_SLASHSLASH: op = OP_QUO; break;
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
if (islvalue(type))
|
|
addop(OP_GETVALUE);
|
|
(void) getorexpr();
|
|
addop(op);
|
|
type = EXPR_RVALUE;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an expression made up of arithmetic or operators.
|
|
* Flags indicating the type of expression found are returned.
|
|
* orexpr = andexpr [ '|' andexpr ] ...
|
|
*/
|
|
S_FUNC int
|
|
getorexpr(void)
|
|
{
|
|
int type; /* type of value found */
|
|
|
|
type = getandexpr();
|
|
while (gettoken() == T_OR) {
|
|
if (islvalue(type))
|
|
addop(OP_GETVALUE);
|
|
(void) getandexpr();
|
|
addop(OP_OR);
|
|
type = EXPR_RVALUE;
|
|
}
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an expression made up of arithmetic and operators.
|
|
* Flags indicating the type of expression found are returned.
|
|
* andexpr = shiftexpr [ '&' shiftexpr ] ...
|
|
*/
|
|
S_FUNC int
|
|
getandexpr(void)
|
|
{
|
|
int type; /* type of value found */
|
|
long op;
|
|
|
|
type = getshiftexpr();
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_AND: op = OP_AND; break;
|
|
case T_TILDE: op = OP_XOR; break;
|
|
case T_BACKSLASH: op = OP_SETMINUS; break;
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
if (islvalue(type))
|
|
addop(OP_GETVALUE);
|
|
(void) getshiftexpr();
|
|
addop(op);
|
|
type = EXPR_RVALUE;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a shift or power expression.
|
|
* Flags indicating the type of expression found are returned.
|
|
* shift = '+' shift
|
|
* | '-' shift
|
|
* | '/' shift
|
|
* | '\' shift
|
|
* | '~' shift
|
|
* | '#' shift
|
|
* | reference '^' shiftexpr
|
|
* | reference '<<' shiftexpr
|
|
* | reference '>>' shiftexpr
|
|
* | reference.
|
|
*/
|
|
S_FUNC int
|
|
getshiftexpr(void)
|
|
{
|
|
int type; /* type of value found */
|
|
long op; /* opcode to generate */
|
|
|
|
op = 0;
|
|
switch (gettoken()) {
|
|
case T_PLUS: op = OP_PLUS; break;
|
|
case T_MINUS: op = OP_NEGATE; break;
|
|
case T_NOT: op = OP_NOT; break;
|
|
case T_DIV: op = OP_INVERT; break;
|
|
case T_BACKSLASH: op = OP_BACKSLASH; break;
|
|
case T_TILDE: op = OP_COMP; break;
|
|
case T_HASH: op = OP_CONTENT; break;
|
|
}
|
|
if (op) {
|
|
(void) getshiftexpr();
|
|
addop(op);
|
|
return EXPR_RVALUE;
|
|
}
|
|
rescantoken();
|
|
type = getreference();
|
|
switch (gettoken()) {
|
|
case T_POWER: op = OP_POWER; break;
|
|
case T_LEFTSHIFT: op = OP_LEFTSHIFT; break;
|
|
case T_RIGHTSHIFT: op = OP_RIGHTSHIFT; break;
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
if (islvalue(type))
|
|
addop(OP_GETVALUE);
|
|
(void) getshiftexpr();
|
|
addop(op);
|
|
return EXPR_RVALUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* set an address or dereference indicator
|
|
* address = '&' term
|
|
* dereference = '*' term
|
|
*/
|
|
S_FUNC int
|
|
getreference(void)
|
|
{
|
|
int type;
|
|
|
|
switch(gettoken()) {
|
|
case T_ANDAND:
|
|
scanerror(T_NULL, "&& used as prefix operator");
|
|
/*FALLTHRU*/
|
|
case T_AND:
|
|
type = getreference();
|
|
addop(OP_PTR);
|
|
type = EXPR_RVALUE;
|
|
break;
|
|
case T_MULT:
|
|
(void) getreference();
|
|
addop(OP_DEREF);
|
|
type = 0;
|
|
break;
|
|
case T_POWER: /* '**' or '^' */
|
|
(void) getreference();
|
|
addop(OP_DEREF);
|
|
addop(OP_DEREF);
|
|
type = 0;
|
|
break;
|
|
default:
|
|
rescantoken();
|
|
type = getincdecexpr();
|
|
}
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* get an increment or decrement expression
|
|
* ++expr, --expr, expr++, expr--
|
|
*/
|
|
S_FUNC int
|
|
getincdecexpr(void)
|
|
{
|
|
int type;
|
|
int tok;
|
|
|
|
type = getterm();
|
|
tok = gettoken();
|
|
if (tok == T_PLUSPLUS || tok == T_MINUSMINUS) {
|
|
if (isrvalue(type))
|
|
scanerror(T_NULL, "Bad ++ usage");
|
|
writeindexop();
|
|
if (tok == T_PLUSPLUS)
|
|
addop(OP_POSTINC);
|
|
else
|
|
addop(OP_POSTDEC);
|
|
for (;;) {
|
|
tok = gettoken();
|
|
switch(tok) {
|
|
case T_PLUSPLUS:
|
|
addop(OP_PREINC);
|
|
continue;
|
|
case T_MINUSMINUS:
|
|
addop(OP_PREDEC);
|
|
continue;
|
|
default:
|
|
addop(OP_POP);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
type = EXPR_RVALUE | EXPR_ASSIGN;
|
|
}
|
|
if (tok == T_NOT) {
|
|
addopfunction(OP_CALL, getbuiltinfunc("fact"), 1);
|
|
tok = gettoken();
|
|
type = EXPR_RVALUE;
|
|
}
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get a single term.
|
|
* Flags indicating the type of value found are returned.
|
|
* term = lvalue
|
|
* | lvalue '[' assignment ']'
|
|
* | lvalue '++'
|
|
* | lvalue '--'
|
|
* | real_number
|
|
* | imaginary_number
|
|
* | '.'
|
|
* | string
|
|
* | '(' assignment ')'
|
|
* | function [ '(' [assignment [',' assignment] ] ')' ]
|
|
* | '!' term
|
|
*/
|
|
S_FUNC int
|
|
getterm(void)
|
|
{
|
|
int type; /* type of term found */
|
|
int oldmode;
|
|
|
|
type = 0;
|
|
switch (gettoken()) {
|
|
case T_NUMBER:
|
|
addopone(OP_NUMBER, tokennumber());
|
|
type = (EXPR_RVALUE | EXPR_CONST);
|
|
break;
|
|
|
|
case T_IMAGINARY:
|
|
addopone(OP_IMAGINARY, tokennumber());
|
|
type = (EXPR_RVALUE | EXPR_CONST);
|
|
break;
|
|
|
|
case T_OLDVALUE:
|
|
addop(OP_OLDVALUE);
|
|
type = 0;
|
|
break;
|
|
|
|
case T_STRING:
|
|
addopone(OP_STRING, tokenstring());
|
|
type = EXPR_RVALUE;
|
|
break;
|
|
|
|
case T_PLUSPLUS:
|
|
if (isrvalue(getterm()))
|
|
scanerror(T_NULL, "Bad ++ usage");
|
|
writeindexop();
|
|
addop(OP_PREINC);
|
|
type = EXPR_ASSIGN;
|
|
break;
|
|
|
|
case T_MINUSMINUS:
|
|
if (isrvalue(getterm()))
|
|
scanerror(T_NULL, "Bad -- usage");
|
|
writeindexop();
|
|
addop(OP_PREDEC);
|
|
type = EXPR_ASSIGN;
|
|
break;
|
|
|
|
case T_LEFTPAREN:
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
type = getexprlist();
|
|
if (gettoken() != T_RIGHTPAREN)
|
|
scanerror(T_SEMICOLON,
|
|
"Missing right parenthesis");
|
|
(void) tokenmode(oldmode);
|
|
break;
|
|
|
|
case T_MAT:
|
|
getonematrix(SYM_UNDEFINED);
|
|
type = EXPR_ASSIGN;
|
|
break;
|
|
|
|
case T_OBJ:
|
|
getobjdeclaration(SYM_UNDEFINED);
|
|
type = EXPR_ASSIGN;
|
|
break;
|
|
|
|
case T_SYMBOL:
|
|
rescantoken();
|
|
type = getidexpr(true, 0);
|
|
break;
|
|
|
|
case T_MULT:
|
|
(void) getterm();
|
|
addop(OP_DEREF);
|
|
type = 0;
|
|
break;
|
|
|
|
case T_POWER: /* '**' or '^' */
|
|
(void) getterm();
|
|
addop(OP_DEREF);
|
|
addop(OP_DEREF);
|
|
type = 0;
|
|
break;
|
|
|
|
case T_GLOBAL:
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_NULL,
|
|
"No identifier after global specifier");
|
|
break;
|
|
}
|
|
rescantoken();
|
|
type = getidexpr(true, T_GLOBAL);
|
|
break;
|
|
|
|
case T_LOCAL:
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_NULL,
|
|
"No identifier after local specifier");
|
|
break;
|
|
}
|
|
rescantoken();
|
|
type = getidexpr(true, T_LOCAL);
|
|
break;
|
|
|
|
case T_STATIC:
|
|
if (gettoken() != T_SYMBOL) {
|
|
scanerror(T_NULL,
|
|
"No identifier after static specifier");
|
|
break;
|
|
}
|
|
rescantoken();
|
|
type = getidexpr(true, T_STATIC);
|
|
break;
|
|
|
|
case T_LEFTBRACKET:
|
|
scanerror(T_NULL, "Left bracket with no preceding lvalue");
|
|
break;
|
|
|
|
case T_PERIOD:
|
|
scanerror(T_NULL, "Period with no preceding lvalue");
|
|
break;
|
|
|
|
default:
|
|
if (iskeyword(type)) {
|
|
scanerror(T_NULL,
|
|
"Expression contains reserved keyword");
|
|
break;
|
|
}
|
|
rescantoken();
|
|
scanerror(T_COMMA, "Missing expression");
|
|
}
|
|
if (type == 0) {
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_LEFTBRACKET:
|
|
rescantoken();
|
|
getmatargs();
|
|
type = 0;
|
|
break;
|
|
case T_PERIOD:
|
|
getelement();
|
|
type = 0;
|
|
break;
|
|
case T_LEFTPAREN:
|
|
scanerror(T_NULL,
|
|
"Function calls not allowed "
|
|
"as expressions");
|
|
/*FALLTHRU*/
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
}
|
|
}
|
|
return type;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read in an identifier expressions.
|
|
* This is a symbol name followed by parenthesis, or by square brackets or
|
|
* element references. The symbol can be a global or a local variable name.
|
|
* Returns the type of expression found.
|
|
*/
|
|
S_FUNC int
|
|
getidexpr(bool okmat, int autodef)
|
|
{
|
|
int type;
|
|
char name[SYMBOLSIZE+1]; /* symbol name */
|
|
int oldmode;
|
|
|
|
type = 0;
|
|
if (!getid(name))
|
|
return type;
|
|
switch (gettoken()) {
|
|
case T_LEFTPAREN:
|
|
oldmode = tokenmode(TM_DEFAULT);
|
|
getcallargs(name);
|
|
(void) tokenmode(oldmode);
|
|
type = 0;
|
|
break;
|
|
case T_ASSIGN:
|
|
if (autodef != T_GLOBAL && autodef != T_LOCAL &&
|
|
autodef != T_STATIC)
|
|
autodef = 1;
|
|
/*FALLTHRU*/
|
|
default:
|
|
rescantoken();
|
|
usesymbol(name, autodef);
|
|
}
|
|
/*
|
|
* Now collect as many element references and matrix index operations
|
|
* as there are following the id.
|
|
*/
|
|
for (;;) {
|
|
switch (gettoken()) {
|
|
case T_LEFTBRACKET:
|
|
rescantoken();
|
|
if (!okmat)
|
|
return type;
|
|
getmatargs();
|
|
type = 0;
|
|
break;
|
|
case T_ARROW:
|
|
addop(OP_DEREF);
|
|
/*FALLTHRU*/
|
|
case T_PERIOD:
|
|
getelement();
|
|
type = 0;
|
|
break;
|
|
case T_LEFTPAREN:
|
|
scanerror(T_NULL,
|
|
"Function calls not allowed "
|
|
"as expressions");
|
|
/*FALLTHRU*/
|
|
default:
|
|
rescantoken();
|
|
return type;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* getsymvalue - return the VALUE of a symbol
|
|
*
|
|
* given:
|
|
* name symbol name
|
|
* v_p pointer to value return
|
|
*
|
|
* returns:
|
|
* symbol type found:
|
|
*
|
|
* SYM_UNDEFINED no such symbol
|
|
* SYM_GLOBAL global symbol found
|
|
*
|
|
* NOTE: This is a special hack to allow some special code in getfilename()
|
|
* to get the value of a symbol. It should NOT be used in the
|
|
* general op code generation / calc code parsing case.
|
|
*/
|
|
S_FUNC int
|
|
getsymvalue(char *name, VALUE *v_p)
|
|
{
|
|
GLOBAL *g_ret; /* global return from findglobal() */
|
|
|
|
/* firewall */
|
|
if (name == NULL || v_p == NULL) {
|
|
return SYM_UNDEFINED;
|
|
}
|
|
|
|
/* look for a global */
|
|
g_ret = findglobal(name);
|
|
if (g_ret != NULL) {
|
|
*v_p = g_ret->g_value;
|
|
return SYM_GLOBAL;
|
|
}
|
|
|
|
/* no such symbol */
|
|
return SYM_UNDEFINED;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read in a filename for a read or write command.
|
|
* Both quoted and unquoted filenames are handled here.
|
|
* The name must be terminated by an end of line or semicolon.
|
|
* Returns true if the filename was successfully parsed.
|
|
*
|
|
* given:
|
|
* name filename to read
|
|
* namelen length of filename buffer including NUL byte
|
|
* once non-NULL => set to true of -once read
|
|
*/
|
|
S_FUNC int
|
|
getfilename(char *name, size_t namelen, bool *once)
|
|
{
|
|
STRING *s;
|
|
char *symstr; /* symbol string */
|
|
VALUE val; /* value of the symbol */
|
|
int i;
|
|
|
|
(void) tokenmode(TM_NEWLINES | TM_ALLSYMS);
|
|
for (i = 2; i > 0; i--) {
|
|
switch (gettoken()) {
|
|
case T_STRING:
|
|
|
|
/* use the value of the literal string */
|
|
s = findstring(tokenstring());
|
|
strlcpy(name, s->s_str, namelen);
|
|
sfree(s);
|
|
break;
|
|
|
|
case T_SYMBOL:
|
|
|
|
/* get the symbol name */
|
|
symstr = tokensymbol();
|
|
|
|
/*
|
|
* special hack - symbols starting with $ are
|
|
* treated as a global variable
|
|
* instead of a literal string.
|
|
*/
|
|
if (symstr[0] == '$') {
|
|
++symstr;
|
|
if (getsymvalue(symstr, &val)) {
|
|
if (val.v_type == V_STR) {
|
|
/* use symbol VALUE string */
|
|
symstr = val.v_str->s_str;
|
|
if (symstr == NULL) {
|
|
math_error(
|
|
"string value pointer is NULL!!");
|
|
not_reached();
|
|
}
|
|
} else {
|
|
math_error(
|
|
"a filename variable must be a string");
|
|
not_reached();
|
|
}
|
|
} else {
|
|
math_error("no such global variable");
|
|
not_reached();
|
|
}
|
|
}
|
|
|
|
/* return symbol name or value of global var string */
|
|
strlcpy(name, symstr, namelen);
|
|
break;
|
|
|
|
case T_NEWLINE:
|
|
|
|
/* found newline */
|
|
rescantoken();
|
|
return 1;
|
|
|
|
default:
|
|
|
|
/* found something unexpected */
|
|
rescantoken();
|
|
return -1;
|
|
}
|
|
|
|
/* deal with -once */
|
|
if (i == 2 && once != NULL) {
|
|
if ((*once = !strcmp(name, "-once")))
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the show command to display useful information
|
|
*/
|
|
S_FUNC void
|
|
getshowstatement(void)
|
|
{
|
|
char name[5];
|
|
long arg, index;
|
|
|
|
switch (gettoken()) {
|
|
case T_SYMBOL:
|
|
strlcpy(name, tokensymbol(), sizeof(name));
|
|
name[4] = '\0';
|
|
/* Yuck! */
|
|
arg = stringindex("buil\000"
|
|
"real\000"
|
|
"func\000"
|
|
"objf\000"
|
|
"conf\000"
|
|
"objt\000"
|
|
"file\000"
|
|
"size\000"
|
|
"erro\000"
|
|
"cust\000"
|
|
"bloc\000"
|
|
"cons\000"
|
|
"glob\000"
|
|
"stat\000"
|
|
"numb\000"
|
|
"redc\000"
|
|
"stri\000"
|
|
"lite\000"
|
|
"opco\000", name);
|
|
break;
|
|
case T_GLOBAL:
|
|
arg = 13; break;
|
|
case T_STATIC:
|
|
arg = 14; break;
|
|
default:
|
|
printf("SHOW command to be followed by at least ");
|
|
printf("four letters of one of:\n");
|
|
printf("\tblocks, builtin, config, constants, ");
|
|
printf("custom, errors, files, functions,\n");
|
|
printf("\tglobaltypes, objfunctions, objtypes, "
|
|
"opcodes, sizes, ");
|
|
printf("realglobals,\n");
|
|
printf("\tstatics, numbers, redcdata, "
|
|
"strings, literals\n");
|
|
rescantoken();
|
|
return;
|
|
|
|
}
|
|
if (arg == 19) {
|
|
if (gettoken() != T_SYMBOL) {
|
|
rescantoken();
|
|
scanerror(T_SEMICOLON,
|
|
"Function name expected for show statement");
|
|
return;
|
|
}
|
|
index = adduserfunc(tokensymbol());
|
|
addopone(OP_SHOW, index + 19);
|
|
return;
|
|
}
|
|
if (arg > 0)
|
|
addopone(OP_SHOW, arg);
|
|
else
|
|
warning("Unknown parameter for show statement");
|
|
}
|
|
|
|
|
|
/*
|
|
* Read in a set of matrix index arguments, surrounded with square brackets.
|
|
* This also handles double square brackets for 'fast indexing'.
|
|
*/
|
|
S_FUNC void
|
|
getmatargs(void)
|
|
{
|
|
int dim;
|
|
|
|
if (gettoken() != T_LEFTBRACKET) {
|
|
scanerror(T_NULL, "Matrix indexing expected");
|
|
return;
|
|
}
|
|
/*
|
|
* Parse all levels of the array reference
|
|
* Look for the 'fast index' first.
|
|
*/
|
|
if (gettoken() == T_LEFTBRACKET) {
|
|
(void) getopassignment();
|
|
if ((gettoken() != T_RIGHTBRACKET) ||
|
|
(gettoken() != T_RIGHTBRACKET)) {
|
|
scanerror(T_NULL, "Bad fast index usage");
|
|
return;
|
|
}
|
|
addop(OP_FIADDR);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
/*
|
|
* Normal indexing with the indexes separated by commas.
|
|
* Initialize the flag in the opcode to assume that the array
|
|
* element will only be referenced for reading. If the parser
|
|
* finds that the element will be referenced for writing, then
|
|
* it will call writeindexop to change the flag in the opcode.
|
|
*/
|
|
dim = 0;
|
|
if (gettoken() == T_RIGHTBRACKET) {
|
|
addoptwo(OP_INDEXADDR, (long) dim, (long) false);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
for (;;) {
|
|
++dim;
|
|
(void) getopassignment();
|
|
switch (gettoken()) {
|
|
case T_RIGHTBRACKET:
|
|
addoptwo(OP_INDEXADDR, (long) dim,
|
|
(long) false);
|
|
return;
|
|
case T_COMMA:
|
|
break;
|
|
default:
|
|
rescantoken();
|
|
scanerror(T_NULL,
|
|
"Missing right bracket in "
|
|
"array reference");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get an element of an object reference.
|
|
* The leading period which introduces the element has already been read.
|
|
*/
|
|
S_FUNC void
|
|
getelement(void)
|
|
{
|
|
long index;
|
|
char name[SYMBOLSIZE+1];
|
|
|
|
if (!getid(name))
|
|
return;
|
|
index = findelement(name);
|
|
if (index < 0) {
|
|
scanerror(T_NULL, "Element \"%s\" is undefined", name);
|
|
return;
|
|
}
|
|
addopone(OP_ELEMADDR, index);
|
|
}
|
|
|
|
|
|
/*
|
|
* Read in a single symbol name and copy its value into the given buffer.
|
|
* Returns true if a valid symbol id was found.
|
|
*/
|
|
S_FUNC bool
|
|
getid(char *buf)
|
|
{
|
|
int type;
|
|
|
|
type = gettoken();
|
|
if (iskeyword(type)) {
|
|
scanerror(T_NULL, "Reserved keyword used as symbol name");
|
|
type = T_SYMBOL;
|
|
*buf = '\0';
|
|
return false;
|
|
}
|
|
if (type != T_SYMBOL) {
|
|
rescantoken();
|
|
scanerror(T_NULL, "Symbol name expected");
|
|
*buf = '\0';
|
|
return false;
|
|
}
|
|
strlcpy(buf, tokensymbol(), SYMBOLSIZE+1);
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Define a symbol name to be of the specified symbol type. The scope
|
|
* of a static variable with the same name is terminated if symtype is
|
|
* global or if symtype is static and the old variable is at the same
|
|
* level. Warnings are issued when a global or local variable is
|
|
* redeclared and when in the same body the variable will be accessible only
|
|
^ with the appropriate specifier.
|
|
*/
|
|
S_FUNC void
|
|
definesymbol(char *name, int symtype)
|
|
{
|
|
switch (symboltype(name)) {
|
|
case SYM_STATIC:
|
|
if (symtype == SYM_GLOBAL || symtype == SYM_STATIC)
|
|
endscope(name, symtype == SYM_GLOBAL);
|
|
break;
|
|
case SYM_GLOBAL:
|
|
if (symtype == SYM_GLOBAL && conf->redecl_warn) {
|
|
warning("redeclaration of global \"%s\"",
|
|
name);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case SYM_LOCAL:
|
|
if (symtype == SYM_LOCAL && conf->redecl_warn) {
|
|
warning("redeclaration of local \"%s\"",
|
|
name);
|
|
return;
|
|
}
|
|
if (symtype == SYM_GLOBAL && conf->dupvar_warn) {
|
|
warning("both local and global \"%s\" defined", name);
|
|
break;
|
|
}
|
|
if (conf->dupvar_warn) {
|
|
warning("both local and static \"%s\" defined", name);
|
|
}
|
|
break;
|
|
case SYM_PARAM:
|
|
if (symtype == SYM_LOCAL && conf->dupvar_warn) {
|
|
warning("both local and parameter \"%s\" defined",
|
|
name);
|
|
break;
|
|
}
|
|
if (symtype == SYM_GLOBAL && conf->dupvar_warn) {
|
|
warning("both global and parameter \"%s\" defined",
|
|
name);
|
|
break;
|
|
}
|
|
if (conf->dupvar_warn) {
|
|
warning("both static and parameter \"%s\" defined",
|
|
name);
|
|
}
|
|
}
|
|
if (symtype == SYM_LOCAL)
|
|
(void) addlocal(name);
|
|
else
|
|
(void) addglobal(name, (symtype == SYM_STATIC));
|
|
}
|
|
|
|
|
|
/*
|
|
* Check a symbol name to see if it is known and generate code to reference it.
|
|
* The symbol can be either a parameter name, a local name, or a global name.
|
|
* If autodef is true, we automatically define the name as a global symbol
|
|
* if it is not yet known.
|
|
*
|
|
* given:
|
|
* name symbol name to be checked
|
|
* autodef 1 => define if symbol is not known
|
|
* T_GLOBAL => get global, define if necessary
|
|
*/
|
|
S_FUNC void
|
|
usesymbol(char *name, int autodef)
|
|
{
|
|
int type;
|
|
|
|
type = symboltype(name);
|
|
if (autodef == T_GLOBAL) {
|
|
if (type == SYM_GLOBAL) {
|
|
warning("Unnecessary global specifier");
|
|
}
|
|
addopptr(OP_GLOBALADDR, (char *) addglobal(name, false));
|
|
return;
|
|
}
|
|
if (autodef == T_STATIC) {
|
|
addopptr(OP_GLOBALADDR, (char *) addglobal(name, true));
|
|
return;
|
|
}
|
|
if (autodef == T_LOCAL) {
|
|
if (type == SYM_LOCAL) {
|
|
warning("Unnecessary local specifier");
|
|
}
|
|
addopone(OP_LOCALADDR, addlocal(name));
|
|
return;
|
|
}
|
|
switch (type) {
|
|
case SYM_LOCAL:
|
|
addopone(OP_LOCALADDR, (long) findlocal(name));
|
|
return;
|
|
case SYM_PARAM:
|
|
addopone(OP_PARAMADDR, (long) findparam(name));
|
|
return;
|
|
case SYM_GLOBAL:
|
|
case SYM_STATIC:
|
|
addopptr(OP_GLOBALADDR, (char *) findglobal(name));
|
|
return;
|
|
}
|
|
/*
|
|
* The symbol is not yet defined.
|
|
* If we are at the top level and we are allowed to, then define it.
|
|
*/
|
|
if ((curfunc->f_name[0] != '*') || !autodef) {
|
|
scanerror(T_NULL, "\"%s\" is undefined", name);
|
|
return;
|
|
}
|
|
(void) addglobal(name, false);
|
|
addopptr(OP_GLOBALADDR, (char *) findglobal(name));
|
|
}
|
|
|
|
|
|
/*
|
|
* Get arguments for a function call.
|
|
* The name and beginning parenthesis has already been seen.
|
|
* callargs = [ [ '&' ] assignment [',' [ '&' ] assignment] ] ')'.
|
|
*
|
|
* given:
|
|
* name name of function
|
|
*/
|
|
S_FUNC void
|
|
getcallargs(char *name)
|
|
{
|
|
long index; /* function index */
|
|
long op; /* opcode to add */
|
|
int argcount; /* number of arguments */
|
|
bool addrflag;
|
|
|
|
op = OP_CALL;
|
|
index = getbuiltinfunc(name);
|
|
if (index < 0) {
|
|
op = OP_USERCALL;
|
|
index = adduserfunc(name);
|
|
}
|
|
if (gettoken() == T_RIGHTPAREN) {
|
|
if (op == OP_CALL)
|
|
builtincheck(index, 0);
|
|
addopfunction(op, index, 0);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
argcount = 0;
|
|
for (;;) {
|
|
argcount++;
|
|
if (gettoken() == T_RIGHTPAREN) {
|
|
addop(OP_UNDEF);
|
|
if (op == OP_CALL)
|
|
builtincheck(index, argcount);
|
|
addopfunction(op, index, argcount);
|
|
return;
|
|
}
|
|
rescantoken();
|
|
if (gettoken() == T_COMMA) {
|
|
addop(OP_UNDEF);
|
|
continue;
|
|
}
|
|
rescantoken();
|
|
addrflag = (gettoken() == T_BACKQUOTE);
|
|
if (!addrflag)
|
|
rescantoken();
|
|
(void) getopassignment();
|
|
if (addrflag) {
|
|
writeindexop();
|
|
}
|
|
if (!addrflag && (op != OP_CALL))
|
|
addop(OP_GETVALUE);
|
|
if (!strcmp(name, "quomod") && argcount > 2)
|
|
writeindexop();
|
|
switch (gettoken()) {
|
|
case T_RIGHTPAREN:
|
|
if (op == OP_CALL)
|
|
builtincheck(index, argcount);
|
|
addopfunction(op, index, argcount);
|
|
return;
|
|
case T_COMMA:
|
|
break;
|
|
default:
|
|
scanerror(T_SEMICOLON,
|
|
"Missing right parenthesis "
|
|
"in function call");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Change the current directory. If no directory is given, assume home.
|
|
*/
|
|
S_FUNC void
|
|
do_changedir(void)
|
|
{
|
|
char *p;
|
|
STRING *s;
|
|
|
|
/* look at the next token */
|
|
(void) tokenmode(TM_NEWLINES | TM_ALLSYMS);
|
|
|
|
/* determine the new directory */
|
|
s = NULL;
|
|
switch (gettoken()) {
|
|
case T_STRING:
|
|
s = findstring(tokenstring());
|
|
p = s->s_str;
|
|
break;
|
|
case T_SYMBOL:
|
|
p = tokensymbol();
|
|
break;
|
|
default:
|
|
p = home;
|
|
}
|
|
|
|
if (p == NULL) {
|
|
fprintf(stderr, "Cannot determine HOME directory\n");
|
|
}
|
|
|
|
/* change to that directory */
|
|
if (chdir(p)) {
|
|
perror(p);
|
|
}
|
|
if (s != NULL)
|
|
sfree(s);
|
|
}
|
|
|
|
|