Jim Tcl
Check-in [7147cf1a39]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:expr: Replace expression engine

Rework the expression engine to use recursive descent evaluation rather than a shunting yard algorithm. Among other things, it is easier to make lazy operators and the ternary operator work correctly.

In particular, the following expression no longer crashes: $(99?9,99?9:*9:999)?9)

And the code is now smaller.

Signed-off-by: Steve Bennett <steveb@workware.net.au>

Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:7147cf1a396e0fbe59294e61cc923c665752da86
User & Date: steveb@workware.net.au 2017-05-23 04:36:56
Context
2017-05-26
04:27
scan: validate -ve XPG3 specifier

Reported-by: Ryan Whitworth <me@ryanwhitworth.com> Signed-off-by: Steve Bennett <steveb@workware.net.au> check-in: 557db6e98d user: steveb@workware.net.au tags: trunk

2017-05-23
04:36
expr: Replace expression engine

Rework the expression engine to use recursive descent evaluation rather than a shunting yard algorithm. Among other things, it is easier to make lazy operators and the ternary operator work correctly.

In particular, the following expression no longer crashes: $(99?9,99?9:*9:999)?9)

And the code is now smaller.

Signed-off-by: Steve Bennett <steveb@workware.net.au> check-in: 7147cf1a39 user: steveb@workware.net.au tags: trunk

04:36
optimisation: when converting to double, may not need stringrep

Minor optimisation

Signed-off-by: Steve Bennett <steveb@workware.net.au> check-in: bdb0c9632e user: steveb@workware.net.au tags: trunk

Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to jim.c.

4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924

4925
4926
4927
4928
4929
4930
4931
....
5553
5554
5555
5556
5557
5558
5559

5560
5561
5562
5563
5564
5565
5566
....
7570
7571
7572
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583

7584
7585
7586
7587
7588
7589
7590
....
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
7642
7643
7644
7645
7646
7647
....
7663
7664
7665
7666
7667
7668
7669



7670


7671
7672
7673
7674
7675



7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690

7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707

7708




7709
7710
7711
7712
7713
7714
7715
7716
7717
....
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
....
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
....
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793


7794




7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
7809
7810
7811
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
7828
7829





7830
7831
7832
7833
7834
7835
7836
7837
7838
7839
7840
....
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897









7898
7899
7900
7901
7902
7903
7904
7905
7906
7907
7908
7909
7910
7911
....
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986

7987
7988
7989






7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003
8004
....
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
8068
8069
....
8127
8128
8129
8130
8131
8132
8133
8134
8135
8136
8137
8138
8139
8140
8141
....
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
8171
8172
....
8176
8177
8178
8179
8180
8181
8182


8183
8184
8185
8186
8187
8188
8189








8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
....
8226
8227
8228
8229
8230
8231
8232
8233
8234
8235
8236
8237
8238
8239
8240
8241
8242
8243
8244
8245
8246

8247
8248

8249
8250
8251

8252
8253
8254
8255
8256
8257
8258
8259
8260
8261
8262
8263
8264


8265
8266
8267
8268
8269



8270
8271
8272
8273
8274
8275
8276
8277
8278

8279
8280
8281
8282
8283
8284
8285
8286
8287
8288
8289
8290
8291


8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
8313
8314
8315
8316
8317
8318
8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329

8330
8331

8332
8333
8334
8335
8336



8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360

8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375


8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
....
8406
8407
8408
8409
8410
8411
8412
8413
8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
8462
8463
8464
8465
8466
8467
8468
8469
8470
8471
8472
8473
8474
8475
8476
8477
8478
8479
8480
8481
8482
8483
....
8627
8628
8629
8630
8631
8632
8633









8634
8635
8636
8637

8638
8639
8640
8641
8642
8643
8644
8645
8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663
8664
8665
8666
....
8668
8669
8670
8671
8672
8673
8674
8675
8676
8677
8678
8679
8680
8681
8682
8683
8684
8685
8686
8687
8688
8689
8690
8691
8692
8693
8694
....
8722
8723
8724
8725
8726
8727
8728
8729
8730
8731
8732

8733
8734
8735

8736
8737

8738
8739
8740
8741

8742
8743

8744





8745
8746
8747
8748
8749
8750
8751
8752
8753
8754
8755
8756
8757
8758
8759
8760
8761
8762
8763
8764
8765
8766
8767
8768
8769
8770
8771
8772
8773







8774
8775
8776
8777
8778
8779


8780
8781
8782
8783

8784
8785
8786
8787
8788
8789
8790

8791
8792
8793
8794
8795
8796
8797
8798
8799
8800
8801
8802
8803
8804
8805
8806
8807
8808
8809
8810
8811
8812
8813
8814
8815
8816
8817
8818
8819
8820
8821
8822
8823
8824
8825
8826
8827
8828
8829

8830
8831
8832
8833

8834
8835
8836



8837
8838
8839
8840



8841
8842


8843
8844



8845
8846
8847
8848
8849
8850
8851

8852
8853
8854
8855
8856
8857
8858
8859
8860

8861
8862



8863
8864

8865
8866
8867



8868
8869
8870
8871
8872


8873
8874


8875
8876
8877
8878
8879
8880




8881
8882




8883
8884
8885
8886
8887
8888

8889
8890
8891
8892
8893
8894






8895
8896
8897


8898
8899
8900
8901
8902




8903
8904
8905
8906
8907
8908
8909
8910
8911
8912






8913
8914
8915
8916
8917
8918
8919
8920
8921
8922
8923
8924
8925
8926
8927
8928
8929
8930
8931



8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943

8944
8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984





8985
8986
8987
8988
8989
8990

8991
8992
8993
8994
8995
8996
8997
8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013
9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026
9027
9028
9029
9030
9031
9032
9033
9034
9035
9036
9037
9038
9039
9040
9041
9042
9043
9044
9045
9046
9047
9048
9049
9050
9051
9052
9053
9054
9055
9056
9057
9058
9059
9060
9061
9062
9063
9064
9065
9066
9067
9068
9069
9070
9071
9072
9073
9074
9075
9076
9077
9078


9079
9080
9081
9082
9083
9084
9085
9086
9087
9088
9089
9090
9091
9092
9093

9094
9095

9096
9097
9098
9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
9122
9123
9124
9125
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137
9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148

9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166


9167
9168
9169
9170
9171


9172
9173



9174
9175
9176

9177
9178
9179


9180



9181
9182





9183
9184
9185
9186
9187
9188
9189
9190
9191


9192
9193
9194
9195
9196
9197
9198
9199


9200
9201
9202


9203


9204
9205
9206
9207


9208
9209



9210
9211
9212
9213
9214
9215
9216
9217
9218
9219
9220
9221
9222
9223
9224
9225
9226
9227
....
9232
9233
9234
9235
9236
9237
9238
9239
9240
9241
9242
9243
9244
9245
9246
9247
9248
9249
9250
9251




9252

9253
9254
9255
9256
9257
9258


9259
9260
9261


9262

9263
9264
9265
9266


9267


9268
9269
9270
9271
















9272















9273

9274
9275
9276
9277
9278
9279
9280
9281
9282
9283
9284
9285
9286


9287
9288
9289








9290
9291
9292
9293
9294
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304
9305
9306
9307
9308
....
9347
9348
9349
9350
9351
9352
9353
9354
9355
9356
9357
9358
9359
9360
9361
9362
9363
9364
9365
9366
9367
9368
9369
9370
9371
9372
9373

9374
9375
9376
9377
9378
9379
9380
9381
9382
9383
9384
9385
9386
9387
9388
9389
9390
9391
9392
9393
9394
9395
9396
9397
9398
9399
9400
9401
9402
9403
9404
9405
9406
9407
9408
9409
9410
9411
9412
9413
9414
9415
9416
9417
9418
9419
9420
9421
9422
9423
9424
9425
9426
9427
9428
9429
9430
9431
9432
9433
9434
9435





































































9436
9437
9438
9439
9440

9441
9442
9443
9444
9445
9446
9447
9448
9449
....
9463
9464
9465
9466
9467
9468
9469
9470
9471
9472
9473
9474
9475
9476
9477
9478
9479
9480
9481
9482
9483
9484
9485
9486
9487
9488
9489
9490
9491
9492
9493
9494
9495
9496
9497
9498
9499
9500
9501
9502
9503
9504
9505
....
9513
9514
9515
9516
9517
9518
9519
9520
9521
9522
9523
9524
9525
9526
9527
9528
9529
9530
9531
9532
9533
9534
9535
9536

9537
9538
9539
9540
9541
9542
9543
9544
9545
9546
9547
9548
9549
9550
9551
9552
9553
9554
9555
9556
9557
9558
9559
9560
9561
9562
9563
9564
9565
9566
9567
9568
9569
9570
9571
9572
9573
9574
9575
9576
9577
9578
9579
9580
9581
9582
9583
9584
9585
9586
9587
9588
9589
9590
9591
9592
9593
9594
9595
9596
9597
9598
9599
9600
9601
9602
9603
9604
9605
9606
9607
9608
9609
9610
9611
9612
9613
9614
9615
9616
9617
9618
9619
9620
9621
9622
9623
9624
9625
9626
9627
9628
9629
9630
9631
9632
9633
9634
9635
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
.....
11818
11819
11820
11821
11822
11823
11824
11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
11845
11846
11847
11848
11849
11850
11851
11852
11853








11854
11855
11856
11857
11858
11859
11860
11861
11862
11863
11864
11865
11866
11867
11868
11869
11870
11871
11872
11873
11874
11875
11876
11877
11878
11879
11880
11881
11882
11883
11884
11885
11886
.....
12872
12873
12874
12875
12876
12877
12878



























12879
12880
12881
12882
12883
12884
12885
.....
13000
13001
13002
13003
13004
13005
13006
13007
13008
13009
13010
13011
13012
13013
13014
13015
13016
13017
13018
13019
13020
13021
13022
13023
13024
13025
13026
13027
13028
13029
13030
13031
13032
13033
13034
13035
13036
13037
13038
13039
13040
13041
13042
13043
13044
13045
13046
13047
13048
13049
13050
13051
13052
13053
13054
13055
13056
13057
13058
13059
13060
13061
13062
13063
13064
13065
13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
13080
13081
13082
13083
13084
13085
13086
.....
13160
13161
13162
13163
13164
13165
13166
13167
13168
13169
13170
13171
13172
13173
13174
13175
13176
13177
13178
13179
13180
13181
13182
13183
13184
13185
13186
13187
13188
13189
13190
13191
13192
13193
13194
13195
    Jim_DecrRefCount(interp, substKeyObjPtr);

    return resObjPtr;
}

static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
{
    Jim_Obj *resultObjPtr;

    if (Jim_EvalExpression(interp, objPtr, &resultObjPtr) == JIM_OK) {
        /* Note that the result has a ref count of 1, but we need a ref count of 0 */
        resultObjPtr->refCount--;
        return resultObjPtr;

    }
    return NULL;
}

/* -----------------------------------------------------------------------------
 * CallFrame
 * ---------------------------------------------------------------------------*/
................................................................................
    if (i->liveList != NULL) {
        objPtr = i->liveList;

        printf("\n-------------------------------------\n");
        printf("Objects still in the free list:\n");
        while (objPtr) {
            const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";


            if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
                printf("%p (%d) %-10s: '%.20s...'\n",
                    (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
            }
            else {
                printf("%p (%d) %-10s: '%s'\n",
................................................................................
 * Expression Parsing
 * ---------------------------------------------------------------------------*/
static int JimParseExprOperator(struct JimParserCtx *pc);
static int JimParseExprNumber(struct JimParserCtx *pc);
static int JimParseExprIrrational(struct JimParserCtx *pc);
static int JimParseExprBoolean(struct JimParserCtx *pc);

/* Exrp's Stack machine operators opcodes. */

/* Binary operators (numbers) */
enum
{
    /* Continues on from the JIM_TT_ space */
    /* Operations */

    JIM_EXPROP_MUL = JIM_TT_EXPR_OP,             /* 20 */
    JIM_EXPROP_DIV,
    JIM_EXPROP_MOD,
    JIM_EXPROP_SUB,
    JIM_EXPROP_ADD,
    JIM_EXPROP_LSHIFT,
    JIM_EXPROP_RSHIFT,
................................................................................
    JIM_EXPROP_LTE,
    JIM_EXPROP_GTE,
    JIM_EXPROP_NUMEQ,
    JIM_EXPROP_NUMNE,
    JIM_EXPROP_BITAND,          /* 35 */
    JIM_EXPROP_BITXOR,
    JIM_EXPROP_BITOR,

    /* Note must keep these together */
    JIM_EXPROP_LOGICAND,        /* 38 */
    JIM_EXPROP_LOGICAND_LEFT,
    JIM_EXPROP_LOGICAND_RIGHT,

    /* and these */
    JIM_EXPROP_LOGICOR,         /* 41 */
    JIM_EXPROP_LOGICOR_LEFT,
    JIM_EXPROP_LOGICOR_RIGHT,

    /* and these */
    /* Ternary operators */
    JIM_EXPROP_TERNARY,         /* 44 */
    JIM_EXPROP_TERNARY_LEFT,
    JIM_EXPROP_TERNARY_RIGHT,

    /* and these */
    JIM_EXPROP_COLON,           /* 47 */
    JIM_EXPROP_COLON_LEFT,
    JIM_EXPROP_COLON_RIGHT,

    JIM_EXPROP_POW,             /* 50 */

/* Binary operators (strings) */
    JIM_EXPROP_STREQ,           /* 51 */
    JIM_EXPROP_STRNE,
    JIM_EXPROP_STRIN,
    JIM_EXPROP_STRNI,

/* Unary operators (numbers) */
    JIM_EXPROP_NOT,             /* 55 */
    JIM_EXPROP_BITNOT,
    JIM_EXPROP_UNARYMINUS,
    JIM_EXPROP_UNARYPLUS,

    /* Functions */
    JIM_EXPROP_FUNC_FIRST,      /* 59 */
    JIM_EXPROP_FUNC_INT = JIM_EXPROP_FUNC_FIRST,
    JIM_EXPROP_FUNC_WIDE,
    JIM_EXPROP_FUNC_ABS,
    JIM_EXPROP_FUNC_DOUBLE,
    JIM_EXPROP_FUNC_ROUND,
    JIM_EXPROP_FUNC_RAND,
    JIM_EXPROP_FUNC_SRAND,

................................................................................
    JIM_EXPROP_FUNC_LOG10,
    JIM_EXPROP_FUNC_SQRT,
    JIM_EXPROP_FUNC_POW,
    JIM_EXPROP_FUNC_HYPOT,
    JIM_EXPROP_FUNC_FMOD,
};




struct JimExprState


{
    Jim_Obj **stack;
    int stacklen;
    int opcode;
    int skip;



};

/* Operators table */
typedef struct Jim_ExprOperator
{
    const char *name;
    int (*funcop) (Jim_Interp *interp, struct JimExprState * e);
    unsigned char precedence;
    unsigned char arity;
    unsigned char lazy;
    unsigned char namelen;
} Jim_ExprOperator;

static void ExprPush(struct JimExprState *e, Jim_Obj *obj)
{

    Jim_IncrRefCount(obj);
    e->stack[e->stacklen++] = obj;
}

static Jim_Obj *ExprPop(struct JimExprState *e)
{
    JimPanic((e->stacklen <= 0, "expr stack underflow"));
    return e->stack[--e->stacklen];
}

static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprState *e)
{
    int intresult = 1;
    int rc = JIM_OK;
    Jim_Obj *A = ExprPop(e);
    double dA, dC = 0;
    jim_wide wA, wC = 0;






    if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
        switch (e->opcode) {
            case JIM_EXPROP_FUNC_INT:
            case JIM_EXPROP_FUNC_WIDE:
            case JIM_EXPROP_FUNC_ROUND:
            case JIM_EXPROP_UNARYPLUS:
                wC = wA;
                break;
            case JIM_EXPROP_FUNC_DOUBLE:
................................................................................
                wC = !wA;
                break;
            default:
                abort();
        }
    }
    else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
        switch (e->opcode) {
            case JIM_EXPROP_FUNC_INT:
            case JIM_EXPROP_FUNC_WIDE:
                wC = dA;
                break;
            case JIM_EXPROP_FUNC_ROUND:
                wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
                break;
................................................................................
            default:
                abort();
        }
    }

    if (rc == JIM_OK) {
        if (intresult) {
            ExprPush(e, Jim_NewIntObj(interp, wC));
        }
        else {
            ExprPush(e, Jim_NewDoubleObj(interp, dC));
        }
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}
................................................................................
{
    unsigned long x;
    JimRandomBytes(interp, &x, sizeof(x));

    return (double)x / (unsigned long)~0;
}

static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *A = ExprPop(e);
    jim_wide wA;







    int rc = Jim_GetWide(interp, A, &wA);
    if (rc == JIM_OK) {
        switch (e->opcode) {
            case JIM_EXPROP_BITNOT:
                ExprPush(e, Jim_NewIntObj(interp, ~wA));
                break;
            case JIM_EXPROP_FUNC_SRAND:
                JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
                ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
                break;
            default:
                abort();
        }
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}

static int JimExprOpNone(Jim_Interp *interp, struct JimExprState *e)
{
    JimPanic((e->opcode != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));

    ExprPush(e, Jim_NewDoubleObj(interp, JimRandDouble(interp)));

    return JIM_OK;
}

#ifdef JIM_MATH_FUNCTIONS
static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprState *e)
{
    int rc;
    Jim_Obj *A = ExprPop(e);
    double dA, dC;






    rc = Jim_GetDouble(interp, A, &dA);
    if (rc == JIM_OK) {
        switch (e->opcode) {
            case JIM_EXPROP_FUNC_SIN:
                dC = sin(dA);
                break;
            case JIM_EXPROP_FUNC_COS:
                dC = cos(dA);
                break;
            case JIM_EXPROP_FUNC_TAN:
................................................................................
                break;
            case JIM_EXPROP_FUNC_SQRT:
                dC = sqrt(dA);
                break;
            default:
                abort();
        }
        ExprPush(e, Jim_NewDoubleObj(interp, dC));
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}
#endif

/* A binary operation on two ints */
static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *B = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    jim_wide wA, wB;
    int rc = JIM_ERR;










    if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
        jim_wide wC;

        rc = JIM_OK;

        switch (e->opcode) {
            case JIM_EXPROP_LSHIFT:
                wC = wA << wB;
                break;
            case JIM_EXPROP_RSHIFT:
                wC = wA >> wB;
                break;
            case JIM_EXPROP_BITAND:
................................................................................
                    unsigned long uA = (unsigned long)wA;
                    unsigned long uB = (unsigned long)wB;
                    const unsigned int S = sizeof(unsigned long) * 8;

                    /* Shift left by the word size or more is undefined. */
                    uB %= S;

                    if (e->opcode == JIM_EXPROP_ROTR) {
                        uB = S - uB;
                    }
                    wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
                    break;
                }
            default:
                abort();
        }
        ExprPush(e, Jim_NewIntObj(interp, wC));

    }

    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return rc;
}


/* A binary operation on two ints or two doubles (or two strings for some ops) */
static int JimExprOpBin(Jim_Interp *interp, struct JimExprState *e)
{
    int rc = JIM_OK;
    double dA, dB, dC = 0;
    jim_wide wA, wB, wC = 0;


    Jim_Obj *B = ExprPop(e);
    Jim_Obj *A = ExprPop(e);







    if ((A->typePtr != &doubleObjType || A->bytes) &&
        (B->typePtr != &doubleObjType || B->bytes) &&
        JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {

        /* Both are ints */

        switch (e->opcode) {
            case JIM_EXPROP_POW:
            case JIM_EXPROP_FUNC_POW:
                if (wA == 0 && wB < 0) {
                    Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
                    rc = JIM_ERR;
                    goto done;
                }
................................................................................
                goto intresult;
            case JIM_EXPROP_NUMNE:
                wC = wA != wB;
                goto intresult;
        }
    }
    if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
        switch (e->opcode) {
#ifndef JIM_MATH_FUNCTIONS
            case JIM_EXPROP_POW:
            case JIM_EXPROP_FUNC_POW:
            case JIM_EXPROP_FUNC_ATAN2:
            case JIM_EXPROP_FUNC_HYPOT:
            case JIM_EXPROP_FUNC_FMOD:
                Jim_SetResultString(interp, "unsupported", -1);
................................................................................
    }
    else {
        /* Handle the string case */

        /* XXX: Could optimise the eq/ne case by checking lengths */
        int i = Jim_StringCompareObj(interp, A, B, 0);

        switch (e->opcode) {
            case JIM_EXPROP_LT:
                wC = i < 0;
                goto intresult;
            case JIM_EXPROP_GT:
                wC = i > 0;
                goto intresult;
            case JIM_EXPROP_LTE:
................................................................................
    /* If we get here, it is an error */
    rc = JIM_ERR;
done:
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);
    return rc;
intresult:
    ExprPush(e, Jim_NewIntObj(interp, wC));
    goto done;
doubleresult:
    ExprPush(e, Jim_NewDoubleObj(interp, dC));
    goto done;
}

static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
{
    int listlen;
    int i;
................................................................................
        if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
            return 1;
        }
    }
    return 0;
}



static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *B = ExprPop(e);
    Jim_Obj *A = ExprPop(e);

    jim_wide wC;









    switch (e->opcode) {
        case JIM_EXPROP_STREQ:
        case JIM_EXPROP_STRNE:
            wC = Jim_StringEqObj(A, B);
            if (e->opcode == JIM_EXPROP_STRNE) {
                wC = !wC;
            }
            break;
        case JIM_EXPROP_STRIN:
            wC = JimSearchList(interp, B, A);
            break;
        case JIM_EXPROP_STRNI:
            wC = !JimSearchList(interp, B, A);
            break;
        default:
            abort();
    }
    ExprPush(e, Jim_NewIntObj(interp, wC));

    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return JIM_OK;
}

................................................................................
    }
    if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
        return b != 0;
    }
    return -1;
}

static int JimExprOpAndLeft(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;

    switch (ExprBool(interp, A)) {
        case 0:
            /* false, so skip RHS opcodes with a 0 result */
            e->skip = JimWideValue(skip);
            ExprPush(e, Jim_NewIntObj(interp, 0));
            break;

        case 1:

            /* true so continue */
            break;


        case -1:
            /* Invalid */

            rc = JIM_ERR;
    }
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, skip);

    return rc;
}

static int JimExprOpOrLeft(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;



    switch (ExprBool(interp, A)) {
        case 0:
            /* false, so do nothing */
            break;




        case 1:
            /* true so skip RHS opcodes with a 1 result */
            e->skip = JimWideValue(skip);
            ExprPush(e, Jim_NewIntObj(interp, 1));
            break;

        case -1:
            /* Invalid */

            rc = JIM_ERR;
            break;
    }
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, skip);

    return rc;
}

static int JimExprOpAndOrRight(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;



    switch (ExprBool(interp, A)) {
        case 0:
            ExprPush(e, Jim_NewIntObj(interp, 0));
            break;

        case 1:
            ExprPush(e, Jim_NewIntObj(interp, 1));
            break;

        case -1:
            /* Invalid */
            rc = JIM_ERR;
            break;
    }
    Jim_DecrRefCount(interp, A);

    return rc;
}

static int JimExprOpTernaryLeft(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *A = ExprPop(e);
    int rc = JIM_OK;

    /* Repush A */
    ExprPush(e, A);

    switch (ExprBool(interp, A)) {
        case 0:
            /* false, skip RHS opcodes */
            e->skip = JimWideValue(skip);
            /* Push a dummy value */
            ExprPush(e, Jim_NewIntObj(interp, 0));
            break;

        case 1:

            /* true so do nothing */
            break;


        case -1:
            /* Invalid */
            rc = JIM_ERR;
            break;



    }
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, skip);

    return rc;
}

static int JimExprOpColonLeft(Jim_Interp *interp, struct JimExprState *e)
{
    Jim_Obj *skip = ExprPop(e);
    Jim_Obj *B = ExprPop(e);
    Jim_Obj *A = ExprPop(e);

    /* No need to check for A as non-boolean */
    if (ExprBool(interp, A)) {
        /* true, so skip RHS opcodes */
        e->skip = JimWideValue(skip);
        /* Repush B as the answer */
        ExprPush(e, B);
    }

    Jim_DecrRefCount(interp, skip);
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return JIM_OK;
}

static int JimExprOpNull(Jim_Interp *interp, struct JimExprState *e)
{
    return JIM_OK;
}

enum
{
    LAZY_NONE,
    LAZY_OP,
    LAZY_LEFT,
    LAZY_RIGHT,
    RIGHT_ASSOC, /* reuse this field for right associativity too */


};

/* name - precedence - arity - opcode
 *
 * This array *must* be kept in sync with the JIM_EXPROP enum.
 *
 * The following macros pre-compute the string length at compile time.
 */
#define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, LAZY_NONE)

static const struct Jim_ExprOperator Jim_ExprOperators[] = {
    OPRINIT("*", 110, 2, JimExprOpBin),
    OPRINIT("/", 110, 2, JimExprOpBin),
    OPRINIT("%", 110, 2, JimExprOpIntBin),

    OPRINIT("-", 100, 2, JimExprOpBin),
................................................................................
    OPRINIT("==", 70, 2, JimExprOpBin),
    OPRINIT("!=", 70, 2, JimExprOpBin),

    OPRINIT("&", 50, 2, JimExprOpIntBin),
    OPRINIT("^", 49, 2, JimExprOpIntBin),
    OPRINIT("|", 48, 2, JimExprOpIntBin),

    OPRINIT_ATTR("&&", 10, 2, NULL, LAZY_OP),
    OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndLeft, LAZY_LEFT),
    OPRINIT_ATTR(NULL, 10, 2, JimExprOpAndOrRight, LAZY_RIGHT),

    OPRINIT_ATTR("||", 9, 2, NULL, LAZY_OP),
    OPRINIT_ATTR(NULL, 9, 2, JimExprOpOrLeft, LAZY_LEFT),
    OPRINIT_ATTR(NULL, 9, 2, JimExprOpAndOrRight, LAZY_RIGHT),

    OPRINIT_ATTR("?", 5, 2, JimExprOpNull, LAZY_OP),
    OPRINIT_ATTR(NULL, 5, 2, JimExprOpTernaryLeft, LAZY_LEFT),
    OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),

    OPRINIT_ATTR(":", 5, 2, JimExprOpNull, LAZY_OP),
    OPRINIT_ATTR(NULL, 5, 2, JimExprOpColonLeft, LAZY_LEFT),
    OPRINIT_ATTR(NULL, 5, 2, JimExprOpNull, LAZY_RIGHT),

    /* Precedence is higher than * and / but lower than ! and ~, and right-associative */
    OPRINIT_ATTR("**", 120, 2, JimExprOpBin, RIGHT_ASSOC),

    OPRINIT("eq", 60, 2, JimExprOpStrBin),
    OPRINIT("ne", 60, 2, JimExprOpStrBin),

    OPRINIT("in", 55, 2, JimExprOpStrBin),
    OPRINIT("ni", 55, 2, JimExprOpStrBin),

    OPRINIT("!", 150, 1, JimExprOpNumUnary),
    OPRINIT("~", 150, 1, JimExprOpIntUnary),
    OPRINIT(NULL, 150, 1, JimExprOpNumUnary),
    OPRINIT(NULL, 150, 1, JimExprOpNumUnary),



    OPRINIT("int", 200, 1, JimExprOpNumUnary),
    OPRINIT("wide", 200, 1, JimExprOpNumUnary),
    OPRINIT("abs", 200, 1, JimExprOpNumUnary),
    OPRINIT("double", 200, 1, JimExprOpNumUnary),
    OPRINIT("round", 200, 1, JimExprOpNumUnary),
    OPRINIT("rand", 200, 0, JimExprOpNone),
    OPRINIT("srand", 200, 1, JimExprOpIntUnary),

#ifdef JIM_MATH_FUNCTIONS
    OPRINIT("sin", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("cos", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("tan", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("asin", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("acos", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("atan", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("atan2", 200, 2, JimExprOpBin),
    OPRINIT("sinh", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("cosh", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("tanh", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("ceil", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("floor", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("exp", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("log", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("log10", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("sqrt", 200, 1, JimExprOpDoubleUnary),
    OPRINIT("pow", 200, 2, JimExprOpBin),
    OPRINIT("hypot", 200, 2, JimExprOpBin),
    OPRINIT("fmod", 200, 2, JimExprOpBin),
#endif
};
#undef OPRINIT
#undef OPRINIT_LAZY

#define JIM_EXPR_OPERATORS_NUM \
    (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))

static int JimParseExpression(struct JimParserCtx *pc)
{
    /* Discard spaces and quoted newline */
................................................................................
            pc->tend = pc->p - 1;
            pc->tt = JIM_TT_EXPR_BOOLEAN;
            return JIM_OK;
        }
    }
    return JIM_ERR;
}










static int JimParseExprOperator(struct JimParserCtx *pc)
{
    int i;

    int bestIdx = -1, bestLen = 0;

    /* Try to get the longest match. */
    for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {
        const char * const opname = Jim_ExprOperators[i].name;
        const int oplen = Jim_ExprOperators[i].namelen;

        if (opname == NULL || opname[0] != pc->p[0]) {
            continue;
        }

        if (oplen > bestLen && strncmp(opname, pc->p, oplen) == 0) {
            bestIdx = i + JIM_TT_EXPR_OP;
            bestLen = oplen;
        }
    }
    if (bestIdx == -1) {
        return JIM_ERR;
    }

    /* Validate paretheses around function arguments */
    if (bestIdx >= JIM_EXPROP_FUNC_FIRST) {
        const char *p = pc->p + bestLen;
        int len = pc->len - bestLen;

        while (len && isspace(UCHAR(*p))) {
            len--;
            p++;
        }
................................................................................
            return JIM_ERR;
        }
    }
    pc->tend = pc->p + bestLen - 1;
    pc->p += bestLen;
    pc->len -= bestLen;

    pc->tt = bestIdx;
    return JIM_OK;
}

static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
{
    static Jim_ExprOperator dummy_op;
    if (opcode < JIM_TT_EXPR_OP) {
        return &dummy_op;
    }
    return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
}

const char *jim_tt_name(int type)
{
    static const char * const tt_names[JIM_TT_EXPR_OP] =
        { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
            "DBL", "BOO", "$()" };
    if (type < JIM_TT_EXPR_OP) {
        return tt_names[type];
................................................................................
    "expression",
    FreeExprInternalRep,
    DupExprInternalRep,
    NULL,
    JIM_TYPE_REFERENCES,
};

/* Expr bytecode structure */
typedef struct ExprByteCode
{
    ScriptToken *token;         /* Tokens array. */

    int len;                    /* Length as number of tokens. */
    int inUse;                  /* Used for sharing. */
} ExprByteCode;


static void ExprFreeByteCode(Jim_Interp *interp, ExprByteCode * expr)

{
    int i;

    for (i = 0; i < expr->len; i++) {

        Jim_DecrRefCount(interp, expr->token[i].objPtr);
    }

    Jim_Free(expr->token);





    Jim_Free(expr);
}

static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
    ExprByteCode *expr = (void *)objPtr->internalRep.ptr;

    if (expr) {
        if (--expr->inUse != 0) {
            return;
        }

        ExprFreeByteCode(interp, expr);
    }
}

static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
    JIM_NOTUSED(interp);
    JIM_NOTUSED(srcPtr);

    /* Just returns an simple string. */
    dupPtr->typePtr = NULL;
}

/* Check if an expr program looks correct
 * Sets an error result on invalid
 */
static int ExprCheckCorrectness(Jim_Interp *interp, Jim_Obj *exprObjPtr, ExprByteCode * expr)







{
    int i;
    int stacklen = 0;
    int ternary = 0;
    int lasttt = JIM_TT_NONE;
    const char *errmsg;



    /* Try to check if there are stack underflows,
     * and make sure at the end of the program there is
     * a single result on the stack. */

    for (i = 0; i < expr->len; i++) {
        ScriptToken *t = &expr->token[i];
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);
        lasttt = t->type;

        stacklen -= op->arity;


        if (stacklen < 0) {
            break;
        }
        if (t->type == JIM_EXPROP_TERNARY || t->type == JIM_EXPROP_TERNARY_LEFT) {
            ternary++;
        }
        else if (t->type == JIM_EXPROP_COLON || t->type == JIM_EXPROP_COLON_LEFT) {
            if (--ternary < 0) {
                /* got : without preceding ? */
                stacklen = 1;
                break;
            }
        }

        /* All operations and operands add one to the stack */
        stacklen++;
    }
    if (stacklen == 1 && ternary == 0) {
        return JIM_OK;
    }

    if (stacklen <= 0) {
        /* Too few args */
        if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
            errmsg = "too few arguments for math function";
            Jim_SetResultString(interp, "too few arguments for math function", -1);
        } else {
            errmsg = "premature end of expression";
        }
    }
    else if (stacklen > 1) {
        if (lasttt >= JIM_EXPROP_FUNC_FIRST) {
            errmsg = "too many arguments for math function";
        } else {
            errmsg = "extra tokens at end of expression";
        }
    }
    else {
        errmsg = "invalid ternary expression";

    }
    Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": %s", exprObjPtr, errmsg);
    return JIM_ERR;
}


/* This procedure converts every occurrence of || and && opereators
 * in lazy unary versions.



 *
 * a b || is converted into:
 *
 * a <offset> |L b |R



 *
 * a b && is converted into:


 *
 * a <offset> &L b &R



 *
 * "|L" checks if 'a' is true:
 *   1) if it is true pushes 1 and skips <offset> instructions to reach
 *      the opcode just after |R.
 *   2) if it is false does nothing.
 * "|R" checks if 'b' is true:
 *   1) if it is true pushes 1, otherwise pushes 0.

 *
 * "&L" checks if 'a' is true:
 *   1) if it is true does nothing.
 *   2) If it is false pushes 0 and skips <offset> instructions to reach
 *      the opcode just after &R
 * "&R" checks if 'a' is true:
 *      if it is true pushes 1, otherwise pushes 0.
 */
static int ExprAddLazyOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)

{
    int i;




    int leftindex, arity, offset;


    /* Search for the end of the first operator */
    leftindex = expr->len - 1;




    arity = 1;
    while (arity) {
        if (leftindex < 0) {
            return JIM_ERR;


        }
        ScriptToken *tt = &expr->token[leftindex];



        if (tt->type >= JIM_TT_EXPR_OP) {
            arity += JimExprOperatorInfoByOpcode(tt->type)->arity;
        }
        arity--;
        leftindex--;




    }
    leftindex++;





    /* Move them up */
    memmove(&expr->token[leftindex + 2], &expr->token[leftindex],
        sizeof(*expr->token) * (expr->len - leftindex));
    expr->len += 2;
    offset = (expr->len - leftindex) - 1;


    /* Now we rely on the fact that the left and right version have opcodes
     * 1 and 2 after the main opcode respectively
     */
    expr->token[leftindex + 1].type = t->type + 1;
    expr->token[leftindex + 1].objPtr = interp->emptyObj;







    expr->token[leftindex].type = JIM_TT_EXPR_INT;
    expr->token[leftindex].objPtr = Jim_NewIntObj(interp, offset);



    /* Now add the 'R' operator */
    expr->token[expr->len].objPtr = interp->emptyObj;
    expr->token[expr->len].type = t->type + 2;
    expr->len++;





    /* Do we need to adjust the skip count for any &L, |L, ?L or :L in the left operand? */
    for (i = leftindex - 1; i > 0; i--) {
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(expr->token[i].type);
        if (op->lazy == LAZY_LEFT) {
            if (JimWideValue(expr->token[i - 1].objPtr) + i - 1 >= leftindex) {
                JimWideValue(expr->token[i - 1].objPtr) += 2;
            }
        }
    }






    return JIM_OK;
}

static int ExprAddOperator(Jim_Interp *interp, ExprByteCode * expr, ParseToken *t)
{
    struct ScriptToken *token = &expr->token[expr->len];
    const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);

    if (op->lazy == LAZY_OP) {
        if (ExprAddLazyOperator(interp, expr, t) != JIM_OK) {
            Jim_SetResultFormatted(interp, "Expression has bad operands to %s", op->name);
            return JIM_ERR;
        }
    }
    else {
        token->objPtr = interp->emptyObj;
        token->type = t->type;
        expr->len++;
    }



    return JIM_OK;
}

/**
 * Returns the index of the COLON_LEFT to the left of 'right_index'
 * taking into account nesting.
 *
 * The expression *must* be well formed, thus a COLON_LEFT will always be found.
 */
static int ExprTernaryGetColonLeftIndex(ExprByteCode *expr, int right_index)
{
    int ternary_count = 1;


    right_index--;

    while (right_index > 1) {
        if (expr->token[right_index].type == JIM_EXPROP_TERNARY_LEFT) {
            ternary_count--;
        }
        else if (expr->token[right_index].type == JIM_EXPROP_COLON_RIGHT) {
            ternary_count++;
        }
        else if (expr->token[right_index].type == JIM_EXPROP_COLON_LEFT && ternary_count == 1) {
            return right_index;
        }
        right_index--;
    }

    /*notreached*/
    return -1;
}

/**
 * Find the left/right indices for the ternary expression to the left of 'right_index'.
 *
 * Returns 1 if found, and fills in *prev_right_index and *prev_left_index.
 * Otherwise returns 0.
 */
static int ExprTernaryGetMoveIndices(ExprByteCode *expr, int right_index, int *prev_right_index, int *prev_left_index)
{
    int i = right_index - 1;
    int ternary_count = 1;

    while (i > 1) {
        if (expr->token[i].type == JIM_EXPROP_TERNARY_LEFT) {
            if (--ternary_count == 0 && expr->token[i - 2].type == JIM_EXPROP_COLON_RIGHT) {
                *prev_right_index = i - 2;
                *prev_left_index = ExprTernaryGetColonLeftIndex(expr, *prev_right_index);
                return 1;
            }
        }
        else if (expr->token[i].type == JIM_EXPROP_COLON_RIGHT) {
            if (ternary_count == 0) {





                return 0;
            }
            ternary_count++;
        }
        i--;
    }

    return 0;
}

/*
* ExprTernaryReorderExpression description
* ========================================
*
* ?: is right-to-left associative which doesn't work with the stack-based
* expression engine. The fix is to reorder the bytecode.
*
* The expression:
*
*    expr 1?2:0?3:4
*
* Has initial bytecode:
*
*    '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '0' (44=COLON_RIGHT)
*    '2' (40=TERNARY_LEFT) '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT)
*
* The fix involves simulating this expression instead:
*
*    expr 1?2:(0?3:4)
*
* With the following bytecode:
*
*    '1' '2' (40=TERNARY_LEFT) '2' (41=TERNARY_RIGHT) '10' (43=COLON_LEFT) '0' '2' (40=TERNARY_LEFT)
*    '3' (41=TERNARY_RIGHT) '2' (43=COLON_LEFT) '4' (44=COLON_RIGHT) (44=COLON_RIGHT)
*
* i.e. The token COLON_RIGHT at index 8 is moved towards the end of the stack, all tokens above 8
*      are shifted down and the skip count of the token JIM_EXPROP_COLON_LEFT at index 5 is
*      incremented by the amount tokens shifted down. The token JIM_EXPROP_COLON_RIGHT that is moved
*      is identified as immediately preceeding a token JIM_EXPROP_TERNARY_LEFT
*
* ExprTernaryReorderExpression works thus as follows :
* - start from the end of the stack
* - while walking towards the beginning of the stack
*     if token=JIM_EXPROP_COLON_RIGHT then
*        find the associated token JIM_EXPROP_TERNARY_LEFT, which allows to
*            find the associated token previous(JIM_EXPROP_COLON_RIGHT)
*            find the associated token previous(JIM_EXPROP_LEFT_RIGHT)
*        if all found then
*            perform the rotation
*            update the skip count of the token previous(JIM_EXPROP_LEFT_RIGHT)
*        end if
*    end if
*
* Note: care has to be taken for nested ternary constructs!!!
*/
static int ExprTernaryReorderExpression(Jim_Interp *interp, ExprByteCode *expr)
{
    int i;

    for (i = expr->len - 1; i > 1; i--) {
        int prev_right_index;
        int prev_left_index;
        int j;
        ScriptToken tmp;

        if (expr->token[i].type != JIM_EXPROP_COLON_RIGHT) {
            continue;
        }

        /* COLON_RIGHT found: get the indexes needed to move the tokens in the stack (if any) */
        if (ExprTernaryGetMoveIndices(expr, i, &prev_right_index, &prev_left_index) == 0) {
            continue;
        }
        if (prev_left_index < 0) {
            return -1;
        }

        /*
        ** rotate tokens down
        **
        ** +->  [i]                         : JIM_EXPROP_COLON_RIGHT
        ** |     |                             |
        ** |     V                             V
        ** |   [...]                        : ...
        ** |     |                             |
        ** |     V                             V
        ** |   [...]                        : ...
        ** |     |                             |
        ** |     V                             V
        ** +-  [prev_right_index]           : JIM_EXPROP_COLON_RIGHT
        */
        tmp = expr->token[prev_right_index];
        for (j = prev_right_index; j < i; j++) {
            expr->token[j] = expr->token[j + 1];
        }


        expr->token[i] = tmp;

        /* Increment the 'skip' count associated to the previous JIM_EXPROP_COLON_LEFT token
         *
         * This is 'colon left increment' = i - prev_right_index
         *
         * [prev_left_index]      : JIM_EXPROP_LEFT_RIGHT
         * [prev_left_index-1]    : skip_count
         *
         */
        JimWideValue(expr->token[prev_left_index-1].objPtr) += (i - prev_right_index);

        /* Adjust for i-- in the loop */
        i++;
    }

    return 0;
}


static ExprByteCode *ExprCreateByteCode(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
{
    Jim_Stack stack;
    ExprByteCode *expr;
    int ok = 1;
    int i;
    int prevtt = JIM_TT_NONE;
    int have_ternary = 0;

    /* -1 for EOL */
    int count = tokenlist->count - 1;

    expr = Jim_Alloc(sizeof(*expr));
    expr->inUse = 1;
    expr->len = 0;

    Jim_InitStack(&stack);

    /* Need extra bytecodes for lazy operators.
     * Also check for the ternary operator
     */
    for (i = 0; i < tokenlist->count; i++) {
        ParseToken *t = &tokenlist->list[i];
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(t->type);

        if (op->lazy == LAZY_OP) {
            count += 2;
            /* Ternary is a lazy op but also needs reordering */
            if (t->type == JIM_EXPROP_TERNARY) {
                have_ternary = 1;
            }
        }
    }

    expr->token = Jim_Alloc(sizeof(ScriptToken) * count);

    for (i = 0; i < tokenlist->count && ok; i++) {
        ParseToken *t = &tokenlist->list[i];

        /* Next token will be stored here */
        struct ScriptToken *token = &expr->token[expr->len];

        if (t->type == JIM_TT_EOL) {
            break;
        }

        if (TOKEN_IS_EXPR_OP(t->type)) {
            const struct Jim_ExprOperator *op;
            ParseToken *tt;

            /* Convert -/+ to unary minus or unary plus if necessary */
            if (prevtt == JIM_TT_NONE || prevtt == JIM_TT_SUBEXPR_START || prevtt == JIM_TT_SUBEXPR_COMMA || prevtt >= JIM_TT_EXPR_OP) {

                if (t->type == JIM_EXPROP_SUB) {
                    t->type = JIM_EXPROP_UNARYMINUS;
                }
                else if (t->type == JIM_EXPROP_ADD) {
                    t->type = JIM_EXPROP_UNARYPLUS;
                }
            }

            op = JimExprOperatorInfoByOpcode(t->type);

            /* Handle precedence */
            while ((tt = Jim_StackPeek(&stack)) != NULL) {
                const struct Jim_ExprOperator *tt_op =
                    JimExprOperatorInfoByOpcode(tt->type);

                /* Note that right-to-left associativity of ?: operator is handled later.
                 */



                if (op->arity != 1 && tt_op->precedence >= op->precedence) {
                    /* Don't reduce if right associative with equal precedence? */
                    if (tt_op->precedence == op->precedence && tt_op->lazy == RIGHT_ASSOC) {
                        break;
                    }


                    if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
                        ok = 0;



                        goto err;
                    }
                    Jim_StackPop(&stack);

                }
                else {
                    break;


                }



            }
            Jim_StackPush(&stack, t);





        }
        else if (t->type == JIM_TT_SUBEXPR_START) {
            Jim_StackPush(&stack, t);
        }
        else if (t->type == JIM_TT_SUBEXPR_END || t->type == JIM_TT_SUBEXPR_COMMA) {
            /* Reduce the expression back to the previous ( or , */
            ok = 0;
            while (Jim_StackLen(&stack)) {
                ParseToken *tt = Jim_StackPop(&stack);



                if (tt->type == JIM_TT_SUBEXPR_START || tt->type == JIM_TT_SUBEXPR_COMMA) {
                    if (t->type == JIM_TT_SUBEXPR_COMMA) {
                        /* Need to push back the previous START or COMMA in the case of comma */
                        Jim_StackPush(&stack, tt);
                    }
                    ok = 1;
                    break;


                }
                if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
                    goto err;


                }


            }
            if (!ok) {
                Jim_SetResultFormatted(interp, "Unexpected close parenthesis in expression: \"%#s\"", exprObjPtr);
                goto err;


            }
        }



        else {
            Jim_Obj *objPtr = NULL;

            /* This is a simple non-operator term, so create and push the appropriate object */
            token->type = t->type;

            /* Two consecutive terms without an operator is invalid */
            if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
                Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", exprObjPtr);
                ok = 0;
                goto err;
            }

            /* Immediately create a double or int object? */
            if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
                char *endptr;
                if (t->type == JIM_TT_EXPR_INT) {
                    objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
................................................................................
                if (endptr != t->token + t->len) {
                    /* Conversion failed, so just store it as a string */
                    Jim_FreeNewObj(interp, objPtr);
                    objPtr = NULL;
                }
            }

            if (objPtr) {
                token->objPtr = objPtr;
            }
            else {
                /* Everything else is stored a simple string term */
                token->objPtr = Jim_NewStringObj(interp, t->token, t->len);
                if (t->type == JIM_TT_CMD) {
                    /* Only commands need source info */
                    JimSetSourceInfo(interp, token->objPtr, fileNameObj, t->line);
                }
            }
            expr->len++;
        }




        prevtt = t->type;

    }

    /* Reduce any remaining subexpr */
    while (Jim_StackLen(&stack)) {
        ParseToken *tt = Jim_StackPop(&stack);



        if (tt->type == JIM_TT_SUBEXPR_START) {
            ok = 0;
            Jim_SetResultString(interp, "Missing close parenthesis", -1);


            goto err;

        }
        if (ExprAddOperator(interp, expr, tt) != JIM_OK) {
            ok = 0;
            goto err;


        }


    }

    if (have_ternary) {
        if (ExprTernaryReorderExpression(interp, expr) != 0) {
















            ok = 0;















            Jim_SetResultString(interp, "Invalid ternary expression", -1);

        }
    }

  err:
    /* Free the stack used for the compilation. */
    Jim_FreeStack(&stack);

    for (i = 0; i < expr->len; i++) {
        Jim_IncrRefCount(expr->token[i].objPtr);
    }

    if (!ok) {
        ExprFreeByteCode(interp, expr);


        return NULL;
    }









    return expr;
}


/* This method takes the string representation of an expression
 * and generates a program for the Expr's stack-based VM. */
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
    int exprTextLen;
    const char *exprText;
    struct JimParserCtx parser;
    struct ExprByteCode *expr;
    ParseTokenList tokenlist;
    int line;
    Jim_Obj *fileNameObj;
    int rc = JIM_ERR;

    /* Try to get information about filename / line number */
    if (objPtr->typePtr == &sourceObjType) {
................................................................................
    if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
        ScriptTokenListFree(&tokenlist);
        Jim_DecrRefCount(interp, fileNameObj);
        return JIM_ERR;
    }

    /* Now create the expression bytecode from the tokenlist */
    expr = ExprCreateByteCode(interp, &tokenlist, objPtr, fileNameObj);

    /* No longer need the token list */
    ScriptTokenListFree(&tokenlist);

    if (!expr) {
        goto err;
    }

#ifdef DEBUG_SHOW_EXPR
    {
        int i;

        printf("==== Expr ====\n");
        for (i = 0; i < expr->len; i++) {
            ScriptToken *t = &expr->token[i];

            printf("[%2d] %s '%s'\n", i, jim_tt_name(t->type), Jim_String(t->objPtr));
        }
    }

#endif

    /* Check program correctness. */
    if (ExprCheckCorrectness(interp, objPtr, expr) != JIM_OK) {
        /* ExprCheckCorrectness set an error in this case */
        ExprFreeByteCode(interp, expr);
        expr = NULL;
        goto err;
    }

    rc = JIM_OK;

  err:
    /* Free the old internal rep and set the new one. */
    Jim_DecrRefCount(interp, fileNameObj);
    Jim_FreeIntRep(interp, objPtr);
    Jim_SetIntRepPtr(objPtr, expr);
    objPtr->typePtr = &exprObjType;
    return rc;
}

static ExprByteCode *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (objPtr->typePtr != &exprObjType) {
        if (SetExprFromAny(interp, objPtr) != JIM_OK) {
            return NULL;
        }
    }
    return (ExprByteCode *) Jim_GetIntRepPtr(objPtr);
}

#ifdef JIM_OPTIMIZATION
static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, const ScriptToken *token)
{
    if (token->type == JIM_TT_EXPR_INT)
        return token->objPtr;
    else if (token->type == JIM_TT_VAR)
        return Jim_GetVariable(interp, token->objPtr, JIM_NONE);
    else if (token->type == JIM_TT_DICTSUGAR)
        return JimExpandDictSugar(interp, token->objPtr);
    else
        return NULL;
}
#endif

/* -----------------------------------------------------------------------------
 * Expressions evaluation.
 * Jim uses a specialized stack-based virtual machine for expressions,
 * that takes advantage of the fact that expr's operators
 * can't be redefined.
 *
 * Jim_EvalExpression() uses the bytecode compiled by
 * SetExprFromAny() method of the "expression" object.
 *
 * On success a Tcl Object containing the result of the evaluation
 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
 * returned.
 * On error the function returns a retcode != to JIM_OK and set a suitable
 * error on the interp.
 * ---------------------------------------------------------------------------*/
#define JIM_EE_STATICSTACK_LEN 10






































































int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr)
{
    ExprByteCode *expr;
    Jim_Obj *staticStack[JIM_EE_STATICSTACK_LEN];
    int i;

    int retcode = JIM_OK;
    struct JimExprState e;

    expr = JimGetExpression(interp, exprObjPtr);
    if (!expr) {
        return JIM_ERR;         /* error in expression. */
    }

#ifdef JIM_OPTIMIZATION
................................................................................
        Jim_Obj *objPtr;

        /* STEP 1 -- Check if there are the conditions to run the specialized
         * version of while */

        switch (expr->len) {
            case 1:
                objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
                if (objPtr) {
                    Jim_IncrRefCount(objPtr);
                    *exprResultPtrPtr = objPtr;
                    return JIM_OK;
                }
                break;

            case 2:
                if (expr->token[1].type == JIM_EXPROP_NOT) {
                    objPtr = JimExprIntValOrVar(interp, &expr->token[0]);

                    if (objPtr && JimIsWide(objPtr)) {
                        *exprResultPtrPtr = JimWideValue(objPtr) ? interp->falseObj : interp->trueObj;
                        Jim_IncrRefCount(*exprResultPtrPtr);
                        return JIM_OK;
                    }
                }
                break;

            case 3:
                objPtr = JimExprIntValOrVar(interp, &expr->token[0]);
                if (objPtr && JimIsWide(objPtr)) {
                    Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, &expr->token[1]);
                    if (objPtr2 && JimIsWide(objPtr2)) {
                        jim_wide wideValueA = JimWideValue(objPtr);
                        jim_wide wideValueB = JimWideValue(objPtr2);
                        int cmpRes;
                        switch (expr->token[2].type) {
                            case JIM_EXPROP_LT:
                                cmpRes = wideValueA < wideValueB;
                                break;
                            case JIM_EXPROP_LTE:
                                cmpRes = wideValueA <= wideValueB;
                                break;
                            case JIM_EXPROP_GT:
................................................................................
                                break;
                            case JIM_EXPROP_NUMNE:
                                cmpRes = wideValueA != wideValueB;
                                break;
                            default:
                                goto noopt;
                        }
                        *exprResultPtrPtr = cmpRes ? interp->trueObj : interp->falseObj;
                        Jim_IncrRefCount(*exprResultPtrPtr);
                        return JIM_OK;
                    }
                }
                break;
        }
    }
noopt:
#endif

    /* In order to avoid that the internal repr gets freed due to
     * shimmering of the exprObjPtr's object, we make the internal rep
     * shared. */
    expr->inUse++;

    /* The stack-based expr VM itself */


    /* Stack allocation. Expr programs have the feature that
     * a program of length N can't require a stack longer than
     * N. */
    if (expr->len > JIM_EE_STATICSTACK_LEN)
        e.stack = Jim_Alloc(sizeof(Jim_Obj *) * expr->len);
    else
        e.stack = staticStack;

    e.stacklen = 0;

    /* Execute every instruction */
    for (i = 0; i < expr->len && retcode == JIM_OK; i++) {
        Jim_Obj *objPtr;

        switch (expr->token[i].type) {
            case JIM_TT_EXPR_INT:
            case JIM_TT_EXPR_DOUBLE:
            case JIM_TT_EXPR_BOOLEAN:
            case JIM_TT_STR:
                ExprPush(&e, expr->token[i].objPtr);
                break;

            case JIM_TT_VAR:
                objPtr = Jim_GetVariable(interp, expr->token[i].objPtr, JIM_ERRMSG);
                if (objPtr) {
                    ExprPush(&e, objPtr);
                }
                else {
                    retcode = JIM_ERR;
                }
                break;

            case JIM_TT_DICTSUGAR:
                objPtr = JimExpandDictSugar(interp, expr->token[i].objPtr);
                if (objPtr) {
                    ExprPush(&e, objPtr);
                }
                else {
                    retcode = JIM_ERR;
                }
                break;

            case JIM_TT_ESC:
                retcode = Jim_SubstObj(interp, expr->token[i].objPtr, &objPtr, JIM_NONE);
                if (retcode == JIM_OK) {
                    ExprPush(&e, objPtr);
                }
                break;

            case JIM_TT_CMD:
                retcode = Jim_EvalObj(interp, expr->token[i].objPtr);
                if (retcode == JIM_OK) {
                    ExprPush(&e, Jim_GetResult(interp));
                }
                break;

            default:{
                    /* Find and execute the operation */
                    e.skip = 0;
                    e.opcode = expr->token[i].type;

                    retcode = JimExprOperatorInfoByOpcode(e.opcode)->funcop(interp, &e);
                    /* Skip some opcodes if necessary */
                    i += e.skip;
                    continue;
                }
        }
    }

    expr->inUse--;

    if (retcode == JIM_OK) {
        *exprResultPtrPtr = ExprPop(&e);
    }
    else {
        for (i = 0; i < e.stacklen; i++) {
            Jim_DecrRefCount(interp, e.stack[i]);
        }
    }
    if (e.stack != staticStack) {
        Jim_Free(e.stack);
    }
    return retcode;
}

int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
{
    Jim_Obj *exprResultPtr;
    int retcode = Jim_EvalExpression(interp, exprObjPtr, &exprResultPtr);

    if (retcode == JIM_OK) {
        switch (ExprBool(interp, exprResultPtr)) {
            case 0:
                *boolPtr = 0;
                break;

            case 1:
                *boolPtr = 1;
                break;

            case -1:
                retcode = JIM_ERR;
                break;
        }
        Jim_DecrRefCount(interp, exprResultPtr);
    }
    return retcode;
}

/* -----------------------------------------------------------------------------
 * ScanFormat String Object
 * ---------------------------------------------------------------------------*/
................................................................................
#ifdef JIM_OPTIMIZATION
    /* Check if the for is on the form:
     *      for ... {$i < CONST} {incr i}
     *      for ... {$i < $j} {incr i}
     */
    if (retval == JIM_OK && boolean) {
        ScriptObj *incrScript;
        ExprByteCode *expr;
        jim_wide stop, currentVal;
        Jim_Obj *objPtr;
        int cmpOffset;

        /* Do it only if there aren't shared arguments */
        expr = JimGetExpression(interp, argv[2]);
        incrScript = JimGetScript(interp, argv[3]);

        /* Ensure proper lengths to start */
        if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
            goto evalstart;
        }
        /* Ensure proper token types. */
        if (incrScript->token[1].type != JIM_TT_ESC ||
            expr->token[0].type != JIM_TT_VAR ||
            (expr->token[1].type != JIM_TT_EXPR_INT && expr->token[1].type != JIM_TT_VAR)) {
            goto evalstart;
        }

        if (expr->token[2].type == JIM_EXPROP_LT) {
            cmpOffset = 0;
        }
        else if (expr->token[2].type == JIM_EXPROP_LTE) {
            cmpOffset = 1;
        }
        else {
            goto evalstart;
        }









        /* Update command must be incr */
        if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
            goto evalstart;
        }

        /* incr, expression must be about the same variable */
        if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->token[0].objPtr)) {
            goto evalstart;
        }

        /* Get the stop condition (must be a variable or integer) */
        if (expr->token[1].type == JIM_TT_EXPR_INT) {
            if (Jim_GetWide(interp, expr->token[1].objPtr, &stop) == JIM_ERR) {
                goto evalstart;
            }
        }
        else {
            stopVarNamePtr = expr->token[1].objPtr;
            Jim_IncrRefCount(stopVarNamePtr);
            /* Keep the compiler happy */
            stop = 0;
        }

        /* Initialization */
        varNamePtr = expr->token[0].objPtr;
        Jim_IncrRefCount(varNamePtr);

        objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
        if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
            goto testcond;
        }

................................................................................
            return JIM_ERR;
        }
    }
    Jim_SetResult(interp, stringObjPtr);
    return JIM_OK;
}




























/* [debug] */
static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#if defined(JIM_DEBUG_COMMAND) && !defined(JIM_BOOTSTRAP)
    static const char * const options[] = {
        "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
        "exprbc", "show",
................................................................................
        script = JimGetScript(interp, argv[2]);
        if (script == NULL)
            return JIM_ERR;
        Jim_SetResultInt(interp, script->len);
        return JIM_OK;
    }
    else if (option == OPT_EXPRLEN) {
        ExprByteCode *expr;

        if (argc != 3) {
            Jim_WrongNumArgs(interp, 2, argv, "expression");
            return JIM_ERR;
        }
        expr = JimGetExpression(interp, argv[2]);
        if (expr == NULL)
            return JIM_ERR;
        Jim_SetResultInt(interp, expr->len);
        return JIM_OK;
    }
    else if (option == OPT_EXPRBC) {
        Jim_Obj *objPtr;
        ExprByteCode *expr;
        int i;

        if (argc != 3) {
            Jim_WrongNumArgs(interp, 2, argv, "expression");
            return JIM_ERR;
        }
        expr = JimGetExpression(interp, argv[2]);
        if (expr == NULL)
            return JIM_ERR;
        objPtr = Jim_NewListObj(interp, NULL, 0);
        for (i = 0; i < expr->len; i++) {
            const char *type;
            const Jim_ExprOperator *op;
            Jim_Obj *obj = expr->token[i].objPtr;

            switch (expr->token[i].type) {
                case JIM_TT_EXPR_INT:
                    type = "int";
                    break;
                case JIM_TT_EXPR_DOUBLE:
                    type = "double";
                    break;
                case JIM_TT_EXPR_BOOLEAN:
                    type = "boolean";
                    break;
                case JIM_TT_CMD:
                    type = "command";
                    break;
                case JIM_TT_VAR:
                    type = "variable";
                    break;
                case JIM_TT_DICTSUGAR:
                    type = "dictsugar";
                    break;
                case JIM_TT_EXPRSUGAR:
                    type = "exprsugar";
                    break;
                case JIM_TT_ESC:
                    type = "subst";
                    break;
                case JIM_TT_STR:
                    type = "string";
                    break;
                default:
                    op = JimExprOperatorInfoByOpcode(expr->token[i].type);
                    if (op == NULL) {
                        type = "private";
                    }
                    else {
                        type = "operator";
                    }
                    obj = Jim_NewStringObj(interp, op ? op->name : "", -1);
                    break;
            }
            Jim_ListAppendElement(interp, objPtr, Jim_NewStringObj(interp, type, -1));
            Jim_ListAppendElement(interp, objPtr, obj);
        }
        Jim_SetResult(interp, objPtr);
        return JIM_OK;
    }
    else {
        Jim_SetResultString(interp,
            "bad option. Valid options are refcount, " "objcount, objects, invstr", -1);
        return JIM_ERR;
    }
................................................................................
        return JIM_ERR;
    }
}

/* [expr] */
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    Jim_Obj *exprResultPtr;
    int retcode;

    if (argc == 2) {
        retcode = Jim_EvalExpression(interp, argv[1], &exprResultPtr);
    }
    else if (argc > 2) {
        Jim_Obj *objPtr;

        objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
        Jim_IncrRefCount(objPtr);
        retcode = Jim_EvalExpression(interp, objPtr, &exprResultPtr);
        Jim_DecrRefCount(interp, objPtr);
    }
    else {
        Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
        return JIM_ERR;
    }
    if (retcode != JIM_OK)
        return retcode;
    Jim_SetResult(interp, exprResultPtr);
    Jim_DecrRefCount(interp, exprResultPtr);
    return JIM_OK;
}

/* [break] */
static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 1) {







<
<
|
<
<
<
>







 







>







 







|
<
<



|
>







 







<
<

<
<
<
<
|
<
<
<
<
<
|
<
<
<
<
|
<
<
<
|

|
|




|
|





<
|







 







>
>
>
|
>
>
|
<
<
<
<
>
>
>






|


|



|
<
>
|
<
|
<
<
<
<
<
<
<
|



<


>

>
>
>
>

|







 







|







 







|


|







 







|

<

>
>

>
>
>
>
|

|

|



|











|

|

|





|


<

>
>
>
>
>



|







 







|









|

<
<


>
>
>
>
>
>
>
>
>






|







 







|








|
<










|




>

<
|
>
>
>
>
>
>







|







 







|







 







|







 







|


|







 







>
>
|

<
|
<


>
>
>
>
>
>
>
>
|



|












|







 







|

|
|
<

<
<
<
<
<
<
<
<
>
|
<
>
|
<
<
>
|

|
<
<
|


|

<
<
<
>
>

<
<
<
<
>
>
>
|
<
<
<
<
<
<
<
<
>
|
<

|
<
<
|


|

<
<
>
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
<
>
|
<
<
<
<
>
>
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|


<
<
<
<
<


<
<
<
<
<
>
>









|







 







<
|
<
<
<
|
<
<
|
<
<
<
|
<
<


|







|
|
|
|



|
|
|
|
|
|
|


|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|



|







 







>
>
>
>
>
>
>
>
>




>
|



<
|

|



|
|
|


|




|







 







|



<
<
<
<
<
<
<
<
<







 







|
|

|
>
|

<
>

<
>


<
|
>
|
|
>
|
>
>
>
>
>





|






|












|
|
|
|
>
>
>
>
>
>
>
|
<
<
<
<
<
>
>
|
<
<
<
>
|
<
<
|
|
|
<
>
|
|

|
|

|
|
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
>

<
<

>

<
<
>
>
>
|
<
|
<
>
>
>

<
>
>

<
>
>
>

<
<
<
<
<
<
>
|
<
<
<
<
<
<
<
<
>

|
>
>
>

<
>

<
<
>
>
>

<
<
<
<
>
>

<
>
>
|
<
<
|
<
<
>
>
>
>
|
<
>
>
>
>
|
<
<
<
<
<
>
|
<
<
<
<
<
>
>
>
>
>
>
|
<
<
>
>
|
<
<
<
<
>
>
>
>
|
<
<
<
<
<
<
|
<
<
>
>
>
>
>
>
|
|
<
<
<
<
<
<
<
<
|
|
|
<
|
<
<
<
<
>
>
>
|
|
|
<
<
<
<
<
<
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
>
>
>
>
>
|
|
<
<
<
<
>
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
|
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<


<
>










|
|
|
|
|
<
<

>
>
|
|
<
<
|
>
>
|
|
>
>
>
|
|
<
>
|
<
<
>
>
|
>
>
>

<
>
>
>
>
>
|
<
<
|
<
<
<
<
<
>
>
|
<
<
<
<
|
<
|
>
>
|
<
<
>
>
|
>
>

<
<
<
>
>

|
>
>
>




<



|
|
<







 







|
<
<
<

|


|


<
|
>
>
>
>
|
>
|
|
<
<
<

>
>
|
<
<
>
>
|
>
|
<
|
|
>
>
|
>
>
|
|
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>



<

|

<
<
<
<
<
<
>
>



>
>
>
>
>
>
>
>



<

|





|







 







|









<
<
<
|
<
<
<
<
<
<
>


<
<
<
<
<
<
<
<











|






|



|

|
|
|
|
|
|







|



|








<

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

<
<
<
>

<







 







|

|
<





|
|


|
<






|

|




|







 







|
<









|




|
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<
<
<
<
<
<
<
<
<
<
<





<
|


|












<







 







|













|
<
<



|


|





>
>
>
>
>
>
>
>







|




|
|




|






|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|












|
<
<








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|







 







<



|






|








<
<







4912
4913
4914
4915
4916
4917
4918


4919



4920
4921
4922
4923
4924
4925
4926
4927
....
5549
5550
5551
5552
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
....
7567
7568
7569
7570
7571
7572
7573
7574


7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
....
7591
7592
7593
7594
7595
7596
7597


7598




7599





7600




7601



7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616

7617
7618
7619
7620
7621
7622
7623
7624
....
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653




7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670

7671
7672

7673







7674
7675
7676
7677

7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
....
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
....
7740
7741
7742
7743
7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
....
7760
7761
7762
7763
7764
7765
7766
7767
7768

7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
7809

7810
7811
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
....
7861
7862
7863
7864
7865
7866
7867
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879


7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
7900
7901
7902
7903
7904
....
7947
7948
7949
7950
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963

7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980

7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
....
8053
8054
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066
8067
....
8125
8126
8127
8128
8129
8130
8131
8132
8133
8134
8135
8136
8137
8138
8139
....
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170
....
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184

8185

8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
8202
8203
8204
8205
8206
8207
8208
8209
8210
8211
8212
8213
8214
8215
8216
8217
8218
8219
8220
....
8232
8233
8234
8235
8236
8237
8238
8239
8240
8241
8242

8243








8244
8245

8246
8247


8248
8249
8250
8251


8252
8253
8254
8255
8256



8257
8258
8259




8260
8261
8262
8263








8264
8265

8266
8267


8268
8269
8270
8271
8272


8273
8274
8275





































8276
8277

8278
8279




8280
8281
8282
8283























8284
8285
8286
8287





8288
8289





8290
8291
8292
8293
8294
8295
8296
8297
8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
....
8322
8323
8324
8325
8326
8327
8328

8329



8330


8331



8332


8333
8334
8335
8336
8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375
8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
....
8532
8533
8534
8535
8536
8537
8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
8551
8552
8553
8554
8555
8556

8557
8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
8568
8569
8570
8571
8572
8573
8574
8575
8576
8577
8578
8579
8580
....
8582
8583
8584
8585
8586
8587
8588
8589
8590
8591
8592









8593
8594
8595
8596
8597
8598
8599
....
8627
8628
8629
8630
8631
8632
8633
8634
8635
8636
8637
8638
8639
8640

8641
8642

8643
8644
8645

8646
8647
8648
8649
8650
8651
8652
8653
8654
8655
8656
8657
8658
8659
8660
8661
8662
8663
8664
8665
8666
8667
8668
8669
8670
8671
8672
8673
8674
8675
8676
8677
8678
8679
8680
8681
8682
8683
8684
8685
8686
8687
8688
8689
8690
8691
8692
8693





8694
8695
8696



8697
8698


8699
8700
8701

8702
8703
8704
8705
8706
8707
8708
8709
8710



8711
8712
























8713

8714
8715


8716
8717
8718


8719
8720
8721
8722

8723

8724
8725
8726
8727

8728
8729
8730

8731
8732
8733
8734






8735
8736








8737
8738
8739
8740
8741
8742
8743

8744
8745


8746
8747
8748
8749




8750
8751
8752

8753
8754
8755


8756


8757
8758
8759
8760
8761

8762
8763
8764
8765
8766





8767
8768





8769
8770
8771
8772
8773
8774
8775


8776
8777
8778




8779
8780
8781
8782
8783






8784


8785
8786
8787
8788
8789
8790
8791
8792








8793
8794
8795

8796




8797
8798
8799
8800
8801
8802









8803
8804






































8805

8806
8807
8808
8809
8810
8811
8812




8813
8814
8815






















































































8816
8817
8818














8819
8820
8821
8822
8823














































8824
8825

8826
8827

8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
8841
8842
8843


8844
8845
8846
8847
8848


8849
8850
8851
8852
8853
8854
8855
8856
8857
8858

8859
8860


8861
8862
8863
8864
8865
8866
8867

8868
8869
8870
8871
8872
8873


8874





8875
8876
8877




8878

8879
8880
8881
8882


8883
8884
8885
8886
8887
8888



8889
8890
8891
8892
8893
8894
8895
8896
8897
8898
8899

8900
8901
8902
8903
8904

8905
8906
8907
8908
8909
8910
8911
....
8916
8917
8918
8919
8920
8921
8922
8923



8924
8925
8926
8927
8928
8929
8930

8931
8932
8933
8934
8935
8936
8937
8938
8939



8940
8941
8942
8943


8944
8945
8946
8947
8948

8949
8950
8951
8952
8953
8954
8955
8956
8957


8958
8959
8960
8961
8962
8963
8964
8965
8966
8967
8968
8969
8970
8971
8972
8973
8974
8975
8976
8977
8978
8979
8980
8981
8982
8983
8984
8985
8986
8987
8988
8989
8990
8991
8992
8993
8994

8995
8996
8997






8998
8999
9000
9001
9002
9003
9004
9005
9006
9007
9008
9009
9010
9011
9012
9013

9014
9015
9016
9017
9018
9019
9020
9021
9022
9023
9024
9025
9026
9027
9028
....
9067
9068
9069
9070
9071
9072
9073
9074
9075
9076
9077
9078
9079
9080
9081
9082
9083



9084






9085
9086
9087








9088
9089
9090
9091
9092
9093
9094
9095
9096
9097
9098
9099
9100
9101
9102
9103
9104
9105
9106
9107
9108
9109
9110
9111
9112
9113
9114
9115
9116
9117
9118
9119
9120
9121
9122
9123
9124
9125
9126
9127
9128
9129
9130
9131
9132
9133
9134
9135
9136
9137

9138
9139
9140
9141
9142
9143
9144
9145
9146
9147
9148
9149
9150
9151
9152
9153
9154
9155
9156
9157
9158
9159
9160
9161
9162
9163
9164
9165
9166
9167
9168
9169
9170
9171
9172
9173
9174
9175
9176
9177
9178
9179
9180
9181
9182
9183
9184
9185
9186
9187
9188
9189
9190
9191
9192
9193
9194
9195
9196
9197
9198
9199
9200
9201
9202
9203
9204
9205
9206
9207
9208
9209



9210
9211

9212
9213
9214
9215
9216
9217
9218
....
9232
9233
9234
9235
9236
9237
9238
9239
9240
9241

9242
9243
9244
9245
9246
9247
9248
9249
9250
9251

9252
9253
9254
9255
9256
9257
9258
9259
9260
9261
9262
9263
9264
9265
9266
9267
9268
9269
9270
9271
9272
....
9280
9281
9282
9283
9284
9285
9286
9287

9288
9289
9290
9291
9292
9293
9294
9295
9296
9297
9298
9299
9300
9301
9302
9303
9304





































































9305
9306











9307
9308
9309
9310
9311

9312
9313
9314
9315
9316
9317
9318
9319
9320
9321
9322
9323
9324
9325
9326
9327

9328
9329
9330
9331
9332
9333
9334
.....
11503
11504
11505
11506
11507
11508
11509
11510
11511
11512
11513
11514
11515
11516
11517
11518
11519
11520
11521
11522
11523
11524


11525
11526
11527
11528
11529
11530
11531
11532
11533
11534
11535
11536
11537
11538
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555
11556
11557
11558
11559
11560
11561
11562
11563
11564
11565
11566
11567
11568
11569
11570
11571
11572
11573
11574
11575
11576
11577
.....
12563
12564
12565
12566
12567
12568
12569
12570
12571
12572
12573
12574
12575
12576
12577
12578
12579
12580
12581
12582
12583
12584
12585
12586
12587
12588
12589
12590
12591
12592
12593
12594
12595
12596
12597
12598
12599
12600
12601
12602
12603
.....
12718
12719
12720
12721
12722
12723
12724
12725
12726
12727
12728
12729
12730
12731
12732
12733
12734
12735
12736
12737
12738


12739
12740
12741
12742
12743
12744
12745
12746
















































12747
12748
12749
12750
12751
12752
12753
12754
.....
12828
12829
12830
12831
12832
12833
12834

12835
12836
12837
12838
12839
12840
12841
12842
12843
12844
12845
12846
12847
12848
12849
12850
12851
12852
12853


12854
12855
12856
12857
12858
12859
12860
    Jim_DecrRefCount(interp, substKeyObjPtr);

    return resObjPtr;
}

static Jim_Obj *JimExpandExprSugar(Jim_Interp *interp, Jim_Obj *objPtr)
{


    if (Jim_EvalExpression(interp, objPtr) == JIM_OK) {



        return Jim_GetResult(interp);
    }
    return NULL;
}

/* -----------------------------------------------------------------------------
 * CallFrame
 * ---------------------------------------------------------------------------*/
................................................................................
    if (i->liveList != NULL) {
        objPtr = i->liveList;

        printf("\n-------------------------------------\n");
        printf("Objects still in the free list:\n");
        while (objPtr) {
            const char *type = objPtr->typePtr ? objPtr->typePtr->name : "string";
            Jim_String(objPtr);

            if (objPtr->bytes && strlen(objPtr->bytes) > 20) {
                printf("%p (%d) %-10s: '%.20s...'\n",
                    (void *)objPtr, objPtr->refCount, type, objPtr->bytes);
            }
            else {
                printf("%p (%d) %-10s: '%s'\n",
................................................................................
 * Expression Parsing
 * ---------------------------------------------------------------------------*/
static int JimParseExprOperator(struct JimParserCtx *pc);
static int JimParseExprNumber(struct JimParserCtx *pc);
static int JimParseExprIrrational(struct JimParserCtx *pc);
static int JimParseExprBoolean(struct JimParserCtx *pc);

/* expr operator opcodes. */


enum
{
    /* Continues on from the JIM_TT_ space */

    /* Binary operators (numbers) */
    JIM_EXPROP_MUL = JIM_TT_EXPR_OP,             /* 20 */
    JIM_EXPROP_DIV,
    JIM_EXPROP_MOD,
    JIM_EXPROP_SUB,
    JIM_EXPROP_ADD,
    JIM_EXPROP_LSHIFT,
    JIM_EXPROP_RSHIFT,
................................................................................
    JIM_EXPROP_LTE,
    JIM_EXPROP_GTE,
    JIM_EXPROP_NUMEQ,
    JIM_EXPROP_NUMNE,
    JIM_EXPROP_BITAND,          /* 35 */
    JIM_EXPROP_BITXOR,
    JIM_EXPROP_BITOR,


    JIM_EXPROP_LOGICAND,        /* 38 */




    JIM_EXPROP_LOGICOR,         /* 39 */





    JIM_EXPROP_TERNARY,         /* 40 */




    JIM_EXPROP_COLON,           /* 41 */



    JIM_EXPROP_POW,             /* 42 */

    /* Binary operators (strings) */
    JIM_EXPROP_STREQ,           /* 43 */
    JIM_EXPROP_STRNE,
    JIM_EXPROP_STRIN,
    JIM_EXPROP_STRNI,

    /* Unary operators (numbers) */
    JIM_EXPROP_NOT,             /* 47 */
    JIM_EXPROP_BITNOT,
    JIM_EXPROP_UNARYMINUS,
    JIM_EXPROP_UNARYPLUS,

    /* Functions */

    JIM_EXPROP_FUNC_INT,      /* 51 */
    JIM_EXPROP_FUNC_WIDE,
    JIM_EXPROP_FUNC_ABS,
    JIM_EXPROP_FUNC_DOUBLE,
    JIM_EXPROP_FUNC_ROUND,
    JIM_EXPROP_FUNC_RAND,
    JIM_EXPROP_FUNC_SRAND,

................................................................................
    JIM_EXPROP_FUNC_LOG10,
    JIM_EXPROP_FUNC_SQRT,
    JIM_EXPROP_FUNC_POW,
    JIM_EXPROP_FUNC_HYPOT,
    JIM_EXPROP_FUNC_FMOD,
};

/* A expression node is either a term or an operator
 * If a node is an operator, 'op' points to the details of the operator and it's terms.
 */
struct JimExprNode {
    int type;       /* JIM_TT_xxx */
    struct Jim_Obj *objPtr;     /* The object for a term, or NULL for an operator */





    struct JimExprNode *left;   /* For all operators */
    struct JimExprNode *right;  /* For binary operators */
    struct JimExprNode *ternary; /* For ternary operator only */
};

/* Operators table */
typedef struct Jim_ExprOperator
{
    const char *name;
    int (*funcop) (Jim_Interp *interp, struct JimExprNode *opnode);
    unsigned char precedence;
    unsigned char arity;
    unsigned char attr;
    unsigned char namelen;
} Jim_ExprOperator;

static Jim_Obj *JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node);

static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node);
static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node);









static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
{
    int intresult = 1;
    int rc = JIM_OK;

    double dA, dC = 0;
    jim_wide wA, wC = 0;
    Jim_Obj *A;

    if ((A = JimExprGetTerm(interp, node->left)) == NULL) {
        return JIM_ERR;
    }

    if ((A->typePtr != &doubleObjType || A->bytes) && JimGetWideNoErr(interp, A, &wA) == JIM_OK) {
        switch (node->type) {
            case JIM_EXPROP_FUNC_INT:
            case JIM_EXPROP_FUNC_WIDE:
            case JIM_EXPROP_FUNC_ROUND:
            case JIM_EXPROP_UNARYPLUS:
                wC = wA;
                break;
            case JIM_EXPROP_FUNC_DOUBLE:
................................................................................
                wC = !wA;
                break;
            default:
                abort();
        }
    }
    else if ((rc = Jim_GetDouble(interp, A, &dA)) == JIM_OK) {
        switch (node->type) {
            case JIM_EXPROP_FUNC_INT:
            case JIM_EXPROP_FUNC_WIDE:
                wC = dA;
                break;
            case JIM_EXPROP_FUNC_ROUND:
                wC = dA < 0 ? (dA - 0.5) : (dA + 0.5);
                break;
................................................................................
            default:
                abort();
        }
    }

    if (rc == JIM_OK) {
        if (intresult) {
            Jim_SetResultInt(interp, wC);
        }
        else {
            Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
        }
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}
................................................................................
{
    unsigned long x;
    JimRandomBytes(interp, &x, sizeof(x));

    return (double)x / (unsigned long)~0;
}

static int JimExprOpIntUnary(Jim_Interp *interp, struct JimExprNode *node)
{

    jim_wide wA;
    Jim_Obj *A;
    int rc;

    if ((A = JimExprGetTerm(interp, node->left)) == NULL) {
        return JIM_ERR;
    }

    rc = Jim_GetWide(interp, A, &wA);
    if (rc == JIM_OK) {
        switch (node->type) {
            case JIM_EXPROP_BITNOT:
                Jim_SetResultInt(interp, ~wA);
                break;
            case JIM_EXPROP_FUNC_SRAND:
                JimPrngSeed(interp, (unsigned char *)&wA, sizeof(wA));
                Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));
                break;
            default:
                abort();
        }
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}

static int JimExprOpNone(Jim_Interp *interp, struct JimExprNode *node)
{
    JimPanic((node->type != JIM_EXPROP_FUNC_RAND, "JimExprOpNone only support rand()"));

    Jim_SetResult(interp, Jim_NewDoubleObj(interp, JimRandDouble(interp)));

    return JIM_OK;
}

#ifdef JIM_MATH_FUNCTIONS
static int JimExprOpDoubleUnary(Jim_Interp *interp, struct JimExprNode *node)
{
    int rc;

    double dA, dC;
    Jim_Obj *A;

    if ((A = JimExprGetTerm(interp, node->left)) == NULL) {
        return JIM_ERR;
    }

    rc = Jim_GetDouble(interp, A, &dA);
    if (rc == JIM_OK) {
        switch (node->type) {
            case JIM_EXPROP_FUNC_SIN:
                dC = sin(dA);
                break;
            case JIM_EXPROP_FUNC_COS:
                dC = cos(dA);
                break;
            case JIM_EXPROP_FUNC_TAN:
................................................................................
                break;
            case JIM_EXPROP_FUNC_SQRT:
                dC = sqrt(dA);
                break;
            default:
                abort();
        }
        Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
    }

    Jim_DecrRefCount(interp, A);

    return rc;
}
#endif

/* A binary operation on two ints */
static int JimExprOpIntBin(Jim_Interp *interp, struct JimExprNode *node)
{


    jim_wide wA, wB;
    int rc = JIM_ERR;
    Jim_Obj *A, *B;

    if ((A = JimExprGetTerm(interp, node->left)) == NULL) {
        return JIM_ERR;
    }
    if ((B = JimExprGetTerm(interp, node->right)) == NULL) {
        Jim_DecrRefCount(interp, A);
        return JIM_ERR;
    }

    if (Jim_GetWide(interp, A, &wA) == JIM_OK && Jim_GetWide(interp, B, &wB) == JIM_OK) {
        jim_wide wC;

        rc = JIM_OK;

        switch (node->type) {
            case JIM_EXPROP_LSHIFT:
                wC = wA << wB;
                break;
            case JIM_EXPROP_RSHIFT:
                wC = wA >> wB;
                break;
            case JIM_EXPROP_BITAND:
................................................................................
                    unsigned long uA = (unsigned long)wA;
                    unsigned long uB = (unsigned long)wB;
                    const unsigned int S = sizeof(unsigned long) * 8;

                    /* Shift left by the word size or more is undefined. */
                    uB %= S;

                    if (node->type == JIM_EXPROP_ROTR) {
                        uB = S - uB;
                    }
                    wC = (unsigned long)(uA << uB) | (uA >> (S - uB));
                    break;
                }
            default:
                abort();
        }
        Jim_SetResultInt(interp, wC);

    }

    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return rc;
}


/* A binary operation on two ints or two doubles (or two strings for some ops) */
static int JimExprOpBin(Jim_Interp *interp, struct JimExprNode *node)
{
    int rc = JIM_OK;
    double dA, dB, dC = 0;
    jim_wide wA, wB, wC = 0;
    Jim_Obj *A, *B;


    if ((A = JimExprGetTerm(interp, node->left)) == NULL) {
        return JIM_ERR;
    }
    if ((B = JimExprGetTerm(interp, node->right)) == NULL) {
        Jim_DecrRefCount(interp, A);
        return JIM_ERR;
    }

    if ((A->typePtr != &doubleObjType || A->bytes) &&
        (B->typePtr != &doubleObjType || B->bytes) &&
        JimGetWideNoErr(interp, A, &wA) == JIM_OK && JimGetWideNoErr(interp, B, &wB) == JIM_OK) {

        /* Both are ints */

        switch (node->type) {
            case JIM_EXPROP_POW:
            case JIM_EXPROP_FUNC_POW:
                if (wA == 0 && wB < 0) {
                    Jim_SetResultString(interp, "exponentiation of zero by negative power", -1);
                    rc = JIM_ERR;
                    goto done;
                }
................................................................................
                goto intresult;
            case JIM_EXPROP_NUMNE:
                wC = wA != wB;
                goto intresult;
        }
    }
    if (Jim_GetDouble(interp, A, &dA) == JIM_OK && Jim_GetDouble(interp, B, &dB) == JIM_OK) {
        switch (node->type) {
#ifndef JIM_MATH_FUNCTIONS
            case JIM_EXPROP_POW:
            case JIM_EXPROP_FUNC_POW:
            case JIM_EXPROP_FUNC_ATAN2:
            case JIM_EXPROP_FUNC_HYPOT:
            case JIM_EXPROP_FUNC_FMOD:
                Jim_SetResultString(interp, "unsupported", -1);
................................................................................
    }
    else {
        /* Handle the string case */

        /* XXX: Could optimise the eq/ne case by checking lengths */
        int i = Jim_StringCompareObj(interp, A, B, 0);

        switch (node->type) {
            case JIM_EXPROP_LT:
                wC = i < 0;
                goto intresult;
            case JIM_EXPROP_GT:
                wC = i > 0;
                goto intresult;
            case JIM_EXPROP_LTE:
................................................................................
    /* If we get here, it is an error */
    rc = JIM_ERR;
done:
    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);
    return rc;
intresult:
    Jim_SetResultInt(interp, wC);
    goto done;
doubleresult:
    Jim_SetResult(interp, Jim_NewDoubleObj(interp, dC));
    goto done;
}

static int JimSearchList(Jim_Interp *interp, Jim_Obj *listObjPtr, Jim_Obj *valObj)
{
    int listlen;
    int i;
................................................................................
        if (Jim_StringEqObj(Jim_ListGetIndex(interp, listObjPtr, i), valObj)) {
            return 1;
        }
    }
    return 0;
}



static int JimExprOpStrBin(Jim_Interp *interp, struct JimExprNode *node)
{

    Jim_Obj *A, *B;

    jim_wide wC;

    if ((A = JimExprGetTerm(interp, node->left)) == NULL) {
        return JIM_ERR;
    }
    if ((B = JimExprGetTerm(interp, node->right)) == NULL) {
        Jim_DecrRefCount(interp, A);
        return JIM_ERR;
    }

    switch (node->type) {
        case JIM_EXPROP_STREQ:
        case JIM_EXPROP_STRNE:
            wC = Jim_StringEqObj(A, B);
            if (node->type == JIM_EXPROP_STRNE) {
                wC = !wC;
            }
            break;
        case JIM_EXPROP_STRIN:
            wC = JimSearchList(interp, B, A);
            break;
        case JIM_EXPROP_STRNI:
            wC = !JimSearchList(interp, B, A);
            break;
        default:
            abort();
    }
    Jim_SetResultInt(interp, wC);

    Jim_DecrRefCount(interp, A);
    Jim_DecrRefCount(interp, B);

    return JIM_OK;
}

................................................................................
    }
    if (Jim_GetBoolean(interp, obj, &b) == JIM_OK) {
        return b != 0;
    }
    return -1;
}

static int JimExprOpAnd(Jim_Interp *interp, struct JimExprNode *node)
{
    /* evaluate left */
    int result = JimExprGetTermBoolean(interp, node->left);










    if (result == 1) {
        /* true so evaluate right */

        result = JimExprGetTermBoolean(interp, node->right);
    }


    if (result == -1) {
        return JIM_ERR;
    }
    Jim_SetResultInt(interp, result);


    return JIM_OK;
}

static int JimExprOpOr(Jim_Interp *interp, struct JimExprNode *node)
{



    /* evaluate left */
    int result = JimExprGetTermBoolean(interp, node->left);





    if (result == 0) {
        /* false so evaluate right */
        result = JimExprGetTermBoolean(interp, node->right);
    }








    if (result == -1) {
        return JIM_ERR;

    }
    Jim_SetResultInt(interp, result);


    return JIM_OK;
}

static int JimExprOpTernary(Jim_Interp *interp, struct JimExprNode *node)
{


    /* evaluate left */
    int result = JimExprGetTermBoolean(interp, node->left);






































    if (result == 1) {
        /* true so select right */

        return JimExprEvalTermNode(interp, node->right);
    }




    else if (result == 0) {
        /* false so select ternary */
        return JimExprEvalTermNode(interp, node->ternary);
    }























    /* error */
    return JIM_ERR;
}






enum
{





    OP_FUNC = 0x0001,        /* function syntax */
    OP_RIGHT_ASSOC = 0x0002, /* right associative */
};

/* name - precedence - arity - opcode
 *
 * This array *must* be kept in sync with the JIM_EXPROP enum.
 *
 * The following macros pre-compute the string length at compile time.
 */
#define OPRINIT_ATTR(N, P, ARITY, F, ATTR) {N, F, P, ARITY, ATTR, sizeof(N) - 1}
#define OPRINIT(N, P, ARITY, F) OPRINIT_ATTR(N, P, ARITY, F, 0)

static const struct Jim_ExprOperator Jim_ExprOperators[] = {
    OPRINIT("*", 110, 2, JimExprOpBin),
    OPRINIT("/", 110, 2, JimExprOpBin),
    OPRINIT("%", 110, 2, JimExprOpIntBin),

    OPRINIT("-", 100, 2, JimExprOpBin),
................................................................................
    OPRINIT("==", 70, 2, JimExprOpBin),
    OPRINIT("!=", 70, 2, JimExprOpBin),

    OPRINIT("&", 50, 2, JimExprOpIntBin),
    OPRINIT("^", 49, 2, JimExprOpIntBin),
    OPRINIT("|", 48, 2, JimExprOpIntBin),


    OPRINIT("&&", 10, 2, JimExprOpAnd),



    OPRINIT("||", 9, 2, JimExprOpOr),


    OPRINIT_ATTR("?", 5, 3, JimExprOpTernary, OP_RIGHT_ASSOC),



    OPRINIT_ATTR(":", 5, 3, NULL, OP_RIGHT_ASSOC),



    /* Precedence is higher than * and / but lower than ! and ~, and right-associative */
    OPRINIT_ATTR("**", 120, 2, JimExprOpBin, OP_RIGHT_ASSOC),

    OPRINIT("eq", 60, 2, JimExprOpStrBin),
    OPRINIT("ne", 60, 2, JimExprOpStrBin),

    OPRINIT("in", 55, 2, JimExprOpStrBin),
    OPRINIT("ni", 55, 2, JimExprOpStrBin),

    OPRINIT_ATTR("!", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
    OPRINIT_ATTR("~", 150, 1, JimExprOpIntUnary, OP_RIGHT_ASSOC),
    OPRINIT_ATTR(" -", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),
    OPRINIT_ATTR(" +", 150, 1, JimExprOpNumUnary, OP_RIGHT_ASSOC),



    OPRINIT_ATTR("int", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("wide", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("abs", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("double", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("round", 200, 1, JimExprOpNumUnary, OP_FUNC),
    OPRINIT_ATTR("rand", 200, 0, JimExprOpNone, OP_FUNC),
    OPRINIT_ATTR("srand", 200, 1, JimExprOpIntUnary, OP_FUNC),

#ifdef JIM_MATH_FUNCTIONS
    OPRINIT_ATTR("sin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("cos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("tan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("asin", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("acos", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("atan", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("atan2", 200, 2, JimExprOpBin, OP_FUNC),
    OPRINIT_ATTR("sinh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("cosh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("tanh", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("ceil", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("floor", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("exp", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("log", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("log10", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("sqrt", 200, 1, JimExprOpDoubleUnary, OP_FUNC),
    OPRINIT_ATTR("pow", 200, 2, JimExprOpBin, OP_FUNC),
    OPRINIT_ATTR("hypot", 200, 2, JimExprOpBin, OP_FUNC),
    OPRINIT_ATTR("fmod", 200, 2, JimExprOpBin, OP_FUNC),
#endif
};
#undef OPRINIT
#undef OPRINIT_ATTR

#define JIM_EXPR_OPERATORS_NUM \
    (sizeof(Jim_ExprOperators)/sizeof(struct Jim_ExprOperator))

static int JimParseExpression(struct JimParserCtx *pc)
{
    /* Discard spaces and quoted newline */
................................................................................
            pc->tend = pc->p - 1;
            pc->tt = JIM_TT_EXPR_BOOLEAN;
            return JIM_OK;
        }
    }
    return JIM_ERR;
}

static const struct Jim_ExprOperator *JimExprOperatorInfoByOpcode(int opcode)
{
    static Jim_ExprOperator dummy_op;
    if (opcode < JIM_TT_EXPR_OP) {
        return &dummy_op;
    }
    return &Jim_ExprOperators[opcode - JIM_TT_EXPR_OP];
}

static int JimParseExprOperator(struct JimParserCtx *pc)
{
    int i;
    const struct Jim_ExprOperator *bestOp = NULL;
    int bestLen = 0;

    /* Try to get the longest match. */
    for (i = 0; i < (signed)JIM_EXPR_OPERATORS_NUM; i++) {

        const struct Jim_ExprOperator *op = &Jim_ExprOperators[i];

        if (op->name[0] != pc->p[0]) {
            continue;
        }

        if (op->namelen > bestLen && strncmp(op->name, pc->p, op->namelen) == 0) {
            bestOp = op;
            bestLen = op->namelen;
        }
    }
    if (bestOp == NULL) {
        return JIM_ERR;
    }

    /* Validate paretheses around function arguments */
    if (bestOp->attr & OP_FUNC) {
        const char *p = pc->p + bestLen;
        int len = pc->len - bestLen;

        while (len && isspace(UCHAR(*p))) {
            len--;
            p++;
        }
................................................................................
            return JIM_ERR;
        }
    }
    pc->tend = pc->p + bestLen - 1;
    pc->p += bestLen;
    pc->len -= bestLen;

    pc->tt = (bestOp - Jim_ExprOperators) + JIM_TT_EXPR_OP;
    return JIM_OK;
}










const char *jim_tt_name(int type)
{
    static const char * const tt_names[JIM_TT_EXPR_OP] =
        { "NIL", "STR", "ESC", "VAR", "ARY", "CMD", "SEP", "EOL", "EOF", "LIN", "WRD", "(((", ")))", ",,,", "INT",
            "DBL", "BOO", "$()" };
    if (type < JIM_TT_EXPR_OP) {
        return tt_names[type];
................................................................................
    "expression",
    FreeExprInternalRep,
    DupExprInternalRep,
    NULL,
    JIM_TYPE_REFERENCES,
};

/* expr tree structure */
struct ExprTree
{
    struct JimExprNode *expr;   /* The first operator or term */
    struct JimExprNode *nodes;  /* Storage of all nodes in the tree */
    int len;                    /* Number of nodes in use */
    int inUse;                  /* Used for sharing. */

};


static void ExprTreeFreeNodes(Jim_Interp *interp, struct JimExprNode *nodes, int num)
{
    int i;

    for (i = 0; i < num; i++) {
        if (nodes[i].objPtr) {
            Jim_DecrRefCount(interp, nodes[i].objPtr);
        }
    }
    Jim_Free(nodes);
}

static void ExprTreeFree(Jim_Interp *interp, struct ExprTree *expr)
{
    ExprTreeFreeNodes(interp, expr->nodes, expr->len);
    Jim_Free(expr);
}

static void FreeExprInternalRep(Jim_Interp *interp, Jim_Obj *objPtr)
{
    struct ExprTree *expr = (void *)objPtr->internalRep.ptr;

    if (expr) {
        if (--expr->inUse != 0) {
            return;
        }

        ExprTreeFree(interp, expr);
    }
}

static void DupExprInternalRep(Jim_Interp *interp, Jim_Obj *srcPtr, Jim_Obj *dupPtr)
{
    JIM_NOTUSED(interp);
    JIM_NOTUSED(srcPtr);

    /* Just returns an simple string. */
    dupPtr->typePtr = NULL;
}

struct ExprBuilder {
    int parencount;                 /* count of outstanding parentheses */
    int level;                      /* recursion depth */
    ParseToken *token;              /* The current token */
    ParseToken *first_token;        /* The first token */
    Jim_Stack stack;                /* stack of pending terms */
    Jim_Obj *exprObjPtr;            /* the original expression */
    Jim_Obj *fileNameObj;           /* filename of the original expression */
    struct JimExprNode *nodes;      /* storage for all nodes */
    struct JimExprNode *next;       /* storage for the next node */
};






#ifdef DEBUG_SHOW_EXPR
static void JimShowExprNode(struct JimExprNode *node, int level)
{



    int i;
    for (i = 0; i < level; i++) {


        printf("  ");
    }
    if (TOKEN_IS_EXPR_OP(node->type)) {

        printf("%s\n", jim_tt_name(node->type));
        if (node->left) {
            JimShowExprNode(node->left, level + 1);
        }
        if (node->right) {
            JimShowExprNode(node->right, level + 1);
        }
        if (node->ternary) {
            JimShowExprNode(node->ternary, level + 1);



        }
    }
























    else {

        printf("[%s] %s\n", jim_tt_name(node->type), Jim_String(node->objPtr));
    }


}
#endif



#define EXPR_UNTIL_CLOSE 0x0001
#define EXPR_FUNC_ARGS   0x0002
#define EXPR_TERNARY     0x0004


/**

 * Parse the subexpression at builder->token and return with the node on the stack.
 * builder->token is advanced to the next unconsumed token.
 * Returns JIM_OK if OK or JIM_ERR on error and leaves a message in the interpreter result.
 *

 * 'precedence' is the precedence of the current operator. Tokens are consumed until an operator
 * with an equal or lower precedence is reached (or strictly lower if right associative).
 *

 * If EXPR_UNTIL_CLOSE is set, the subexpression extends up to and including the next close parenthesis.
 * If EXPR_FUNC_ARGS is set, multiple subexpressions (terms) are expected separated by comma
 * If EXPR_TERNARY is set, two subexpressions (terms) are expected separated by colon
 *






 * 'exp_numterms' indicates how many terms are expected. Normally this is 1, but may be more for EXPR_FUNC_ARGS and EXPR_TERNARY.
 */








static int ExprTreeBuildTree(Jim_Interp *interp, struct ExprBuilder *builder, int precedence, int flags, int exp_numterms)
{
    int rc;
    struct JimExprNode *node;
    /* Calculate the stack length expected after pushing the number of expected terms */
    int exp_stacklen = builder->stack.len + exp_numterms;


    builder->level++;



    while (builder->token->type != JIM_TT_EOL) {
        ParseToken *t = builder->token++;
        int prevtt;





        if (t == builder->first_token) {
            prevtt = JIM_TT_NONE;
        }

        else {
            prevtt = t[-1].type;
        }





        if (t->type == JIM_TT_SUBEXPR_START) {
            if (builder->stack.len == exp_stacklen) {
                Jim_SetResultFormatted(interp, "unexpected open parenthesis in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }

            builder->parencount++;
            rc = ExprTreeBuildTree(interp, builder, 0, EXPR_UNTIL_CLOSE, 1);
            if (rc != JIM_OK) {
                return rc;
            }





            /* A complete subexpression is on the stack */
        }





        else if (t->type == JIM_TT_SUBEXPR_END) {
            if (!(flags & EXPR_UNTIL_CLOSE)) {
                if (builder->stack.len == exp_stacklen && builder->level > 1) {
                    builder->token--;
                    builder->level--;
                    return JIM_OK;
                }


                Jim_SetResultFormatted(interp, "unexpected closing parenthesis in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }




            builder->parencount--;
            if (builder->stack.len == exp_stacklen) {
                /* Return with the expected number of subexpressions on the stack */
                break;
            }






        }


        else if (t->type == JIM_TT_SUBEXPR_COMMA) {
            if (!(flags & EXPR_FUNC_ARGS)) {
                if (builder->stack.len == exp_stacklen) {
                    /* handle the comma back at the parent level */
                    builder->token--;
                    builder->level--;
                    return JIM_OK;
                }








                Jim_SetResultFormatted(interp, "unexpected comma in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }

            else {




                /* If we see more terms than expected, it is an error */
                if (builder->stack.len > exp_stacklen) {
                    Jim_SetResultFormatted(interp, "too many arguments to math function");
                    return JIM_ERR;
                }
            }









            /* just go onto the next arg */
        }






































        else if (t->type == JIM_EXPROP_COLON) {

            if (!(flags & EXPR_TERNARY)) {
                if (builder->level != 1) {
                    /* handle the comma back at the parent level */
                    builder->token--;
                    builder->level--;
                    return JIM_OK;
                }




                Jim_SetResultFormatted(interp, ": without ? in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;
            }






















































































            if (builder->stack.len == exp_stacklen) {
                /* handle the comma back at the parent level */
                builder->token--;














                builder->level--;
                return JIM_OK;
            }
            /* just go onto the next term */
        }














































        else if (TOKEN_IS_EXPR_OP(t->type)) {
            const struct Jim_ExprOperator *op;


            /* Convert -/+ to unary minus or unary plus if necessary */

            if (TOKEN_IS_EXPR_OP(prevtt) || TOKEN_IS_EXPR_START(prevtt)) {
                if (t->type == JIM_EXPROP_SUB) {
                    t->type = JIM_EXPROP_UNARYMINUS;
                }
                else if (t->type == JIM_EXPROP_ADD) {
                    t->type = JIM_EXPROP_UNARYPLUS;
                }
            }

            op = JimExprOperatorInfoByOpcode(t->type);

            if (op->precedence < precedence || (!(op->attr & OP_RIGHT_ASSOC) && op->precedence == precedence)) {
                /* next op is lower precedence, or equal and left associative, so done here */
                builder->token--;
                break;
            }



            if (op->attr & OP_FUNC) {
                if (builder->token->type != JIM_TT_SUBEXPR_START) {
                    Jim_SetResultString(interp, "missing arguments for math function", -1);
                    return JIM_ERR;


                }
                builder->token++;
                if (op->arity == 0) {
                    if (builder->token->type != JIM_TT_SUBEXPR_END) {
                        Jim_SetResultString(interp, "too many arguments for math function", -1);
                        return JIM_ERR;
                    }
                    builder->token++;
                    goto noargs;
                }

                builder->parencount++;



                /* This will push left and return right */
                rc = ExprTreeBuildTree(interp, builder, 0, EXPR_FUNC_ARGS | EXPR_UNTIL_CLOSE, op->arity);
            }
            else if (t->type == JIM_EXPROP_TERNARY) {
                /* Collect the two arguments to the ternary operator */
                rc = ExprTreeBuildTree(interp, builder, op->precedence, EXPR_TERNARY, 2);
            }

            else {
                /* Recursively handle everything on the right until we see a precendence <= op->precedence or == and right associative
                 * and push that on the term stack
                 */
                rc = ExprTreeBuildTree(interp, builder, op->precedence, 0, 1);
            }








            if (rc != JIM_OK) {
                return rc;
            }






noargs:
            node = builder->next++;
            node->type = t->type;



            if (op->arity >= 3) {
                node->ternary = Jim_StackPop(&builder->stack);
            }
            if (op->arity >= 2) {
                node->right = Jim_StackPop(&builder->stack);
            }



            if (op->arity >= 1) {
                node->left = Jim_StackPop(&builder->stack);
            }

            /* Now push the node */
            Jim_StackPush(&builder->stack, node);
        }
        else {
            Jim_Obj *objPtr = NULL;

            /* This is a simple non-operator term, so create and push the appropriate object */


            /* Two consecutive terms without an operator is invalid */
            if (!TOKEN_IS_EXPR_START(prevtt) && !TOKEN_IS_EXPR_OP(prevtt)) {
                Jim_SetResultFormatted(interp, "missing operator in expression: \"%#s\"", builder->exprObjPtr);
                return JIM_ERR;

            }

            /* Immediately create a double or int object? */
            if (t->type == JIM_TT_EXPR_INT || t->type == JIM_TT_EXPR_DOUBLE) {
                char *endptr;
                if (t->type == JIM_TT_EXPR_INT) {
                    objPtr = Jim_NewIntObj(interp, jim_strtoull(t->token, &endptr));
................................................................................
                if (endptr != t->token + t->len) {
                    /* Conversion failed, so just store it as a string */
                    Jim_FreeNewObj(interp, objPtr);
                    objPtr = NULL;
                }
            }

            if (!objPtr) {



                /* Everything else is stored a simple string term */
                objPtr = Jim_NewStringObj(interp, t->token, t->len);
                if (t->type == JIM_TT_CMD) {
                    /* Only commands need source info */
                    JimSetSourceInfo(interp, objPtr, builder->fileNameObj, t->line);
                }
            }


            /* Now push a term node */
            node = builder->next++;
            node->objPtr = objPtr;
            Jim_IncrRefCount(node->objPtr);
            node->type = t->type;
            Jim_StackPush(&builder->stack, node);
        }
    }




    if (builder->stack.len == exp_stacklen) {
        builder->level--;
        return JIM_OK;


    }

    if ((flags & EXPR_FUNC_ARGS)) {
        Jim_SetResultFormatted(interp, "too %s arguments for math function", (builder->stack.len < exp_stacklen) ? "few" : "many");
    }

    else {
        if (builder->stack.len < exp_stacklen) {
            if (builder->level == 0) {
                Jim_SetResultFormatted(interp, "empty expression");
            }
            else {
                Jim_SetResultFormatted(interp, "syntax error in expression \"%#s\": premature end of expression", builder->exprObjPtr);
            }
        }


        else {
            Jim_SetResultFormatted(interp, "extra terms after expression");
        }
    }

    return JIM_ERR;
}

static struct ExprTree *ExprTreeCreateTree(Jim_Interp *interp, const ParseTokenList *tokenlist, Jim_Obj *exprObjPtr, Jim_Obj *fileNameObj)
{
    struct ExprTree *expr;
    struct ExprBuilder builder;
    int rc;
    struct JimExprNode *top;

    builder.parencount = 0;
    builder.level = 0;
    builder.token = builder.first_token = tokenlist->list;
    builder.exprObjPtr = exprObjPtr;
    builder.fileNameObj = fileNameObj;
    /* The bytecode will never produce more nodes than there are tokens - 1 (for EOL)*/
    builder.nodes = malloc(sizeof(struct JimExprNode) * (tokenlist->count - 1));
    memset(builder.nodes, 0, sizeof(struct JimExprNode) * (tokenlist->count - 1));
    builder.next = builder.nodes;
    Jim_InitStack(&builder.stack);

    rc = ExprTreeBuildTree(interp, &builder, 0, 0, 1);

    if (rc == JIM_OK) {
        top = Jim_StackPop(&builder.stack);

        if (builder.parencount) {
            Jim_SetResultString(interp, "missing close parenthesis", -1);
            rc = JIM_ERR;
        }
    }


    /* Free the stack used for the compilation. */
    Jim_FreeStack(&builder.stack);







    if (rc != JIM_OK) {
        ExprTreeFreeNodes(interp, builder.nodes, builder.next - builder.nodes);
        return NULL;
    }

    expr = Jim_Alloc(sizeof(*expr));
    expr->inUse = 1;
    expr->expr = top;
    expr->nodes = builder.nodes;
    expr->len = builder.next - builder.nodes;

    assert(expr->len <= tokenlist->count - 1);

    return expr;
}


/* This method takes the string representation of an expression
 * and generates a program for the expr engine */
static int SetExprFromAny(Jim_Interp *interp, struct Jim_Obj *objPtr)
{
    int exprTextLen;
    const char *exprText;
    struct JimParserCtx parser;
    struct ExprTree *expr;
    ParseTokenList tokenlist;
    int line;
    Jim_Obj *fileNameObj;
    int rc = JIM_ERR;

    /* Try to get information about filename / line number */
    if (objPtr->typePtr == &sourceObjType) {
................................................................................
    if (JimParseCheckMissing(interp, parser.missing.ch) == JIM_ERR) {
        ScriptTokenListFree(&tokenlist);
        Jim_DecrRefCount(interp, fileNameObj);
        return JIM_ERR;
    }

    /* Now create the expression bytecode from the tokenlist */
    expr = ExprTreeCreateTree(interp, &tokenlist, objPtr, fileNameObj);

    /* No longer need the token list */
    ScriptTokenListFree(&tokenlist);

    if (!expr) {
        goto err;
    }

#ifdef DEBUG_SHOW_EXPR



    printf("==== Expr ====\n");






    JimShowExprNode(expr->expr, 0);
#endif









    rc = JIM_OK;

  err:
    /* Free the old internal rep and set the new one. */
    Jim_DecrRefCount(interp, fileNameObj);
    Jim_FreeIntRep(interp, objPtr);
    Jim_SetIntRepPtr(objPtr, expr);
    objPtr->typePtr = &exprObjType;
    return rc;
}

static struct ExprTree *JimGetExpression(Jim_Interp *interp, Jim_Obj *objPtr)
{
    if (objPtr->typePtr != &exprObjType) {
        if (SetExprFromAny(interp, objPtr) != JIM_OK) {
            return NULL;
        }
    }
    return (struct ExprTree *) Jim_GetIntRepPtr(objPtr);
}

#ifdef JIM_OPTIMIZATION
static Jim_Obj *JimExprIntValOrVar(Jim_Interp *interp, struct JimExprNode *node)
{
    if (node->type == JIM_TT_EXPR_INT)
        return node->objPtr;
    else if (node->type == JIM_TT_VAR)
        return Jim_GetVariable(interp, node->objPtr, JIM_NONE);
    else if (node->type == JIM_TT_DICTSUGAR)
        return JimExpandDictSugar(interp, node->objPtr);
    else
        return NULL;
}
#endif

/* -----------------------------------------------------------------------------
 * Expressions evaluation.
 * Jim uses a recursive evaluation engine for expressions,
 * that takes advantage of the fact that expr's operators
 * can't be redefined.
 *
 * Jim_EvalExpression() uses the expression tree compiled by
 * SetExprFromAny() method of the "expression" object.
 *
 * On success a Tcl Object containing the result of the evaluation
 * is stored into expResultPtrPtr (having refcount of 1), and JIM_OK is
 * returned.
 * On error the function returns a retcode != to JIM_OK and set a suitable
 * error on the interp.
 * ---------------------------------------------------------------------------*/


static int JimExprEvalTermNode(Jim_Interp *interp, struct JimExprNode *node)
{
    if (TOKEN_IS_EXPR_OP(node->type)) {
        const struct Jim_ExprOperator *op = JimExprOperatorInfoByOpcode(node->type);
        return op->funcop(interp, node);
    }
    else {
        Jim_Obj *objPtr;

        /* A term */
        switch (node->type) {
            case JIM_TT_EXPR_INT:
            case JIM_TT_EXPR_DOUBLE:
            case JIM_TT_EXPR_BOOLEAN:
            case JIM_TT_STR:
                Jim_SetResult(interp, node->objPtr);
                return JIM_OK;

            case JIM_TT_VAR:
                objPtr = Jim_GetVariable(interp, node->objPtr, JIM_ERRMSG);
                if (objPtr) {
                    Jim_SetResult(interp, objPtr);
                    return JIM_OK;
                }
                return JIM_ERR;

            case JIM_TT_DICTSUGAR:
                objPtr = JimExpandDictSugar(interp, node->objPtr);
                if (objPtr) {
                    Jim_SetResult(interp, objPtr);
                    return JIM_OK;
                }
                return JIM_ERR;

            case JIM_TT_ESC:
                if (Jim_SubstObj(interp, node->objPtr, &objPtr, JIM_NONE) == JIM_OK) {
                    Jim_SetResult(interp, objPtr);
                    return JIM_OK;
                }
                return JIM_ERR;

            case JIM_TT_CMD:
                return Jim_EvalObj(interp, node->objPtr);

            default:
                /* Should never get here */
                return JIM_ERR;
        }
    }
}

static Jim_Obj *JimExprGetTerm(Jim_Interp *interp, struct JimExprNode *node)
{
    if (JimExprEvalTermNode(interp, node) == JIM_OK) {
        Jim_Obj *objPtr = Jim_GetResult(interp);
        Jim_IncrRefCount(objPtr);
        return objPtr;
    }
    return NULL;
}

static int JimExprGetTermBoolean(Jim_Interp *interp, struct JimExprNode *node)
{
    if (JimExprEvalTermNode(interp, node) == JIM_OK) {
        return ExprBool(interp, Jim_GetResult(interp));
    }
    return -1;
}

int Jim_EvalExpression(Jim_Interp *interp, Jim_Obj *exprObjPtr)
{



    struct ExprTree *expr;
    int retcode = JIM_OK;


    expr = JimGetExpression(interp, exprObjPtr);
    if (!expr) {
        return JIM_ERR;         /* error in expression. */
    }

#ifdef JIM_OPTIMIZATION
................................................................................
        Jim_Obj *objPtr;

        /* STEP 1 -- Check if there are the conditions to run the specialized
         * version of while */

        switch (expr->len) {
            case 1:
                objPtr = JimExprIntValOrVar(interp, expr->expr);
                if (objPtr) {
                    Jim_SetResult(interp, objPtr);

                    return JIM_OK;
                }
                break;

            case 2:
                if (expr->expr->type == JIM_EXPROP_NOT) {
                    objPtr = JimExprIntValOrVar(interp, expr->expr->left);

                    if (objPtr && JimIsWide(objPtr)) {
                        Jim_SetResult(interp, JimWideValue(objPtr) ? interp->falseObj : interp->trueObj);

                        return JIM_OK;
                    }
                }
                break;

            case 3:
                objPtr = JimExprIntValOrVar(interp, expr->expr->left);
                if (objPtr && JimIsWide(objPtr)) {
                    Jim_Obj *objPtr2 = JimExprIntValOrVar(interp, expr->expr->right);
                    if (objPtr2 && JimIsWide(objPtr2)) {
                        jim_wide wideValueA = JimWideValue(objPtr);
                        jim_wide wideValueB = JimWideValue(objPtr2);
                        int cmpRes;
                        switch (expr->expr->type) {
                            case JIM_EXPROP_LT:
                                cmpRes = wideValueA < wideValueB;
                                break;
                            case JIM_EXPROP_LTE:
                                cmpRes = wideValueA <= wideValueB;
                                break;
                            case JIM_EXPROP_GT:
................................................................................
                                break;
                            case JIM_EXPROP_NUMNE:
                                cmpRes = wideValueA != wideValueB;
                                break;
                            default:
                                goto noopt;
                        }
                        Jim_SetResult(interp, cmpRes ? interp->trueObj : interp->falseObj);

                        return JIM_OK;
                    }
                }
                break;
        }
    }
noopt:
#endif

    /* In order to avoid the internal repr being freed due to
     * shimmering of the exprObjPtr's object, we make the internal rep
     * shared. */
    expr->inUse++;

    /* Evaluate with the recursive expr engine */
    retcode = JimExprEvalTermNode(interp, expr->expr);






































































    expr->inUse--;












    return retcode;
}

int Jim_GetBoolFromExpr(Jim_Interp *interp, Jim_Obj *exprObjPtr, int *boolPtr)
{

    int retcode = Jim_EvalExpression(interp, exprObjPtr);

    if (retcode == JIM_OK) {
        switch (ExprBool(interp, Jim_GetResult(interp))) {
            case 0:
                *boolPtr = 0;
                break;

            case 1:
                *boolPtr = 1;
                break;

            case -1:
                retcode = JIM_ERR;
                break;
        }

    }
    return retcode;
}

/* -----------------------------------------------------------------------------
 * ScanFormat String Object
 * ---------------------------------------------------------------------------*/
................................................................................
#ifdef JIM_OPTIMIZATION
    /* Check if the for is on the form:
     *      for ... {$i < CONST} {incr i}
     *      for ... {$i < $j} {incr i}
     */
    if (retval == JIM_OK && boolean) {
        ScriptObj *incrScript;
        struct ExprTree *expr;
        jim_wide stop, currentVal;
        Jim_Obj *objPtr;
        int cmpOffset;

        /* Do it only if there aren't shared arguments */
        expr = JimGetExpression(interp, argv[2]);
        incrScript = JimGetScript(interp, argv[3]);

        /* Ensure proper lengths to start */
        if (incrScript == NULL || incrScript->len != 3 || !expr || expr->len != 3) {
            goto evalstart;
        }
        /* Ensure proper token types. */
        if (incrScript->token[1].type != JIM_TT_ESC) {


            goto evalstart;
        }

        if (expr->expr->type == JIM_EXPROP_LT) {
            cmpOffset = 0;
        }
        else if (expr->expr->type == JIM_EXPROP_LTE) {
            cmpOffset = 1;
        }
        else {
            goto evalstart;
        }

        if (expr->expr->left->type != JIM_TT_VAR) {
            goto evalstart;
        }

        if (expr->expr->right->type != JIM_TT_VAR && expr->expr->right->type != JIM_TT_EXPR_INT) {
            goto evalstart;
        }

        /* Update command must be incr */
        if (!Jim_CompareStringImmediate(interp, incrScript->token[1].objPtr, "incr")) {
            goto evalstart;
        }

        /* incr, expression must be about the same variable */
        if (!Jim_StringEqObj(incrScript->token[2].objPtr, expr->expr->left->objPtr)) {
            goto evalstart;
        }

        /* Get the stop condition (must be a variable or integer) */
        if (expr->expr->right->type == JIM_TT_EXPR_INT) {
            if (Jim_GetWide(interp, expr->expr->right->objPtr, &stop) == JIM_ERR) {
                goto evalstart;
            }
        }
        else {
            stopVarNamePtr = expr->expr->right->objPtr;
            Jim_IncrRefCount(stopVarNamePtr);
            /* Keep the compiler happy */
            stop = 0;
        }

        /* Initialization */
        varNamePtr = expr->expr->left->objPtr;
        Jim_IncrRefCount(varNamePtr);

        objPtr = Jim_GetVariable(interp, varNamePtr, JIM_NONE);
        if (objPtr == NULL || Jim_GetWide(interp, objPtr, &currentVal) != JIM_OK) {
            goto testcond;
        }

................................................................................
            return JIM_ERR;
        }
    }
    Jim_SetResult(interp, stringObjPtr);
    return JIM_OK;
}

#if defined(JIM_DEBUG_COMMAND) && !defined(JIM_BOOTSTRAP)
/**
 * Returns a zero-refcount list describing the expression at 'node'
 */
static Jim_Obj *JimGetExprAsList(Jim_Interp *interp, struct JimExprNode *node)
{
    Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);

    Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, jim_tt_name(node->type), -1));
    if (TOKEN_IS_EXPR_OP(node->type)) {
        if (node->left) {
            Jim_ListAppendElement(interp, listObjPtr, JimGetExprAsList(interp, node->left));
        }
        if (node->right) {
            Jim_ListAppendElement(interp, listObjPtr, JimGetExprAsList(interp, node->right));
        }
        if (node->ternary) {
            Jim_ListAppendElement(interp, listObjPtr, JimGetExprAsList(interp, node->ternary));
        }
    }
    else {
        Jim_ListAppendElement(interp, listObjPtr, node->objPtr);
    }
    return listObjPtr;
}
#endif /* JIM_DEBUG_COMMAND && !JIM_BOOTSTRAP */

/* [debug] */
static int Jim_DebugCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
#if defined(JIM_DEBUG_COMMAND) && !defined(JIM_BOOTSTRAP)
    static const char * const options[] = {
        "refcount", "objcount", "objects", "invstr", "scriptlen", "exprlen",
        "exprbc", "show",
................................................................................
        script = JimGetScript(interp, argv[2]);
        if (script == NULL)
            return JIM_ERR;
        Jim_SetResultInt(interp, script->len);
        return JIM_OK;
    }
    else if (option == OPT_EXPRLEN) {
        struct ExprTree *expr;

        if (argc != 3) {
            Jim_WrongNumArgs(interp, 2, argv, "expression");
            return JIM_ERR;
        }
        expr = JimGetExpression(interp, argv[2]);
        if (expr == NULL)
            return JIM_ERR;
        Jim_SetResultInt(interp, expr->len);
        return JIM_OK;
    }
    else if (option == OPT_EXPRBC) {
        struct ExprTree *expr;



        if (argc != 3) {
            Jim_WrongNumArgs(interp, 2, argv, "expression");
            return JIM_ERR;
        }
        expr = JimGetExpression(interp, argv[2]);
        if (expr == NULL)
            return JIM_ERR;
















































        Jim_SetResult(interp, JimGetExprAsList(interp, expr->expr));
        return JIM_OK;
    }
    else {
        Jim_SetResultString(interp,
            "bad option. Valid options are refcount, " "objcount, objects, invstr", -1);
        return JIM_ERR;
    }
................................................................................
        return JIM_ERR;
    }
}

/* [expr] */
static int Jim_ExprCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{

    int retcode;

    if (argc == 2) {
        retcode = Jim_EvalExpression(interp, argv[1]);
    }
    else if (argc > 2) {
        Jim_Obj *objPtr;

        objPtr = Jim_ConcatObj(interp, argc - 1, argv + 1);
        Jim_IncrRefCount(objPtr);
        retcode = Jim_EvalExpression(interp, objPtr);
        Jim_DecrRefCount(interp, objPtr);
    }
    else {
        Jim_WrongNumArgs(interp, 1, argv, "expression ?...?");
        return JIM_ERR;
    }
    if (retcode != JIM_OK)
        return retcode;


    return JIM_OK;
}

/* [break] */
static int Jim_BreakCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
    if (argc != 1) {

Changes to jim.h.

812
813
814
815
816
817
818
819
820
821
822
823
824
825
826

/* return code object */
JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
        int *intPtr);

/* expression object */
JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
        Jim_Obj *exprObjPtr, Jim_Obj **exprResultPtrPtr);
JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
        Jim_Obj *exprObjPtr, int *boolPtr);

/* boolean object */
JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
        int *booleanPtr);








|







812
813
814
815
816
817
818
819
820
821
822
823
824
825
826

/* return code object */
JIM_EXPORT int Jim_GetReturnCode (Jim_Interp *interp, Jim_Obj *objPtr,
        int *intPtr);

/* expression object */
JIM_EXPORT int Jim_EvalExpression (Jim_Interp *interp,
        Jim_Obj *exprObjPtr);
JIM_EXPORT int Jim_GetBoolFromExpr (Jim_Interp *interp,
        Jim_Obj *exprObjPtr, int *boolPtr);

/* boolean object */
JIM_EXPORT int Jim_GetBoolean(Jim_Interp *interp, Jim_Obj *objPtr,
        int *booleanPtr);

Changes to regtest.tcl.

321
322
323
324
325
326
327





328
329
330
puts "TEST 45 PASSED"

# REGTEST 46
# scan with no stringrep
catch {scan $(1) $(1)}
puts "TEST 46 PASSED"






# TAKE THE FOLLOWING puts AS LAST LINE

puts "--- ALL TESTS PASSED ---"







>
>
>
>
>



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
puts "TEST 45 PASSED"

# REGTEST 46
# scan with no stringrep
catch {scan $(1) $(1)}
puts "TEST 46 PASSED"

# REGTEST 47
# Invalid ternary expression
catch {set a $(99?9,99?9:*9:999)?9)}
puts "TEST 47 PASSED"

# TAKE THE FOLLOWING puts AS LAST LINE

puts "--- ALL TESTS PASSED ---"

Changes to tests/expr.test.

94
95
96
97
98
99
100








101
102
103
104
105
106
107
test expr-2.3 "Ternary operator - missing third term" -body {
	expr {1 ? 2}
} -returnCodes error -match glob -result *

test expr-2.4 "Ternary operator - missing question" -body {
	expr {1 : 2}
} -returnCodes error -match glob -result *









test expr-3.1 "in, ni operators" {
	set l {a b c d}
	set c C
	list [expr {"a" in $l}] [expr {$c in $l}] [expr {"b" ni $l}] [expr {$c ni $l}]
} {1 0 0 1}








>
>
>
>
>
>
>
>







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
test expr-2.3 "Ternary operator - missing third term" -body {
	expr {1 ? 2}
} -returnCodes error -match glob -result *

test expr-2.4 "Ternary operator - missing question" -body {
	expr {1 : 2}
} -returnCodes error -match glob -result *

test expr-2.5 "Ternary operator with -ve values" {
	expr {-1?-2:-3}
} -2

test expr-2.6 "Ternary operator with -ve values" {
	expr {0?-2:-3}
} -3

test expr-3.1 "in, ni operators" {
	set l {a b c d}
	set c C
	list [expr {"a" in $l}] [expr {$c in $l}] [expr {"b" ni $l}] [expr {$c ni $l}]
} {1 0 0 1}