diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-20 22:04:48 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-20 22:04:48 +0200 |
| commit | c62aef166f1163524fe5d63a5c00571a75731dcf (patch) | |
| tree | 866a160fa47481bb4c9cae8adf1fa790e5955373 | |
| parent | 3fb1a395ce147d364c811842ca87935caf75517a (diff) | |
internal/rpn/rpn.go: fix stack state persistence for ParseAndEvaluate
- Reset currentStack at start of each ParseAndEvaluate call
- This ensures test isolation while allowing incremental operations
- Added tests for EvalOperator for incremental RPN evaluation
- All tests pass, coverage 79.2% for rpn package
| -rw-r--r-- | coverage.out | 238 | ||||
| -rw-r--r-- | internal/repl/repl.go | 26 | ||||
| -rw-r--r-- | internal/rpn/rpn.go | 102 | ||||
| -rw-r--r-- | internal/rpn/rpn_test.go | 135 |
4 files changed, 495 insertions, 6 deletions
diff --git a/coverage.out b/coverage.out new file mode 100644 index 0000000..0434ec0 --- /dev/null +++ b/coverage.out @@ -0,0 +1,238 @@ +mode: set +codeberg.org/snonux/perc/internal/rpn/operations.go:14.49,18.2 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:23.46,25.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:25.16,27.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:29.2,30.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:30.16,32.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:34.2,35.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:39.51,41.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:41.16,43.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:45.2,46.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:46.16,48.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:50.2,51.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:55.51,57.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:57.16,59.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:61.2,62.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:62.16,64.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:66.2,67.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:71.49,73.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:73.16,75.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:77.2,78.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:78.16,80.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:82.2,82.12 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:82.12,84.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:86.2,87.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:91.48,93.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:93.16,95.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:97.2,98.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:98.16,100.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:102.2,103.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:107.49,109.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:109.16,111.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:113.2,114.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:114.16,116.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:118.2,118.12 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:118.12,120.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:122.2,123.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:129.46,131.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:131.16,133.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:134.2,135.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:139.47,140.21 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:140.21,142.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:145.2,157.12 8 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:161.46,163.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:163.16,165.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:166.2,166.12 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:170.57,171.22 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:171.22,173.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:175.2,177.27 3 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:177.27,178.12 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:178.12,180.4 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:181.3,181.38 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:183.2,183.20 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:190.70,191.16 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:191.16,193.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:195.2,195.21 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:195.21,197.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:199.2,200.16 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:200.16,202.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:204.2,204.38 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:209.67,210.16 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:210.16,212.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:214.2,215.13 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:215.13,217.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:219.2,220.12 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:225.56,226.16 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:226.16,228.3 1 0 +codeberg.org/snonux/perc/internal/rpn/operations.go:230.2,231.14 2 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:231.14,233.3 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:234.2,234.12 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:239.54,241.2 1 1 +codeberg.org/snonux/perc/internal/rpn/operations.go:245.39,247.2 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:18.35,25.2 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:29.62,31.17 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:31.17,33.3 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:36.2,40.36 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:40.36,42.22 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:42.22,47.28 4 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:47.28,50.30 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:50.30,52.20 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:52.20,54.7 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:55.6,55.67 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:55.67,57.7 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:58.6,58.63 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:66.2,66.36 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:66.36,73.15 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:73.15,78.30 4 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:78.30,84.19 4 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:84.19,86.58 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:86.58,88.7 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:91.6,91.65 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:97.2,98.22 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:98.22,100.3 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:102.2,102.27 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:106.38,109.2 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:112.57,115.27 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:115.27,117.3 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:118.2,120.31 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:120.31,122.19 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:122.19,124.4 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:127.3,127.60 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:127.60,128.33 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:128.33,130.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:131.4,132.12 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:136.3,136.16 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:137.12,138.43 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:138.43,140.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:141.12,142.48 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:142.48,144.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:145.12,146.48 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:146.48,148.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:149.12,150.46 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:150.46,152.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:153.12,154.45 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:154.45,156.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:157.12,158.46 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:158.46,160.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:161.14,162.43 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:162.43,164.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:165.15,166.44 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:166.44,168.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:169.14,170.43 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:170.43,172.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:173.37,175.18 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:175.18,177.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:178.4,178.22 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:179.15,181.18 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:181.18,183.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:184.4,184.22 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:185.16,187.39 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:188.12,189.74 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:190.11,193.14 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:193.14,195.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:195.10,197.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:202.2,202.22 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:202.22,204.3 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:207.2,207.21 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:207.21,210.17 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:210.17,212.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:213.3,213.21 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:218.2,219.37 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:219.37,221.3 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:224.2,224.21 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:224.21,227.17 2 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:227.17,229.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:230.3,230.21 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:234.2,235.39 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:240.60,243.31 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:243.31,244.60 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:244.60,246.12 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:249.3,249.16 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:250.12,251.43 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:251.43,253.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:254.12,255.48 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:255.48,257.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:258.12,259.48 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:259.48,261.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:262.12,263.46 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:263.46,265.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:266.12,267.45 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:267.45,269.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:270.12,271.46 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:271.46,273.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:274.14,275.43 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:275.43,277.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:278.15,279.44 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:279.44,281.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:282.14,283.43 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:283.43,285.5 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:286.37,287.28 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:288.15,289.32 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:290.16,292.39 2 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:293.11,295.14 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:295.14,297.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:297.10,299.5 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:303.2,303.26 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:308.55,309.27 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:309.27,311.3 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:313.2,313.12 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:314.11,315.51 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:315.51,317.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:318.11,319.56 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:319.56,321.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:322.11,323.56 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:323.56,325.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:326.11,327.54 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:327.54,329.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:330.11,331.53 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:331.53,333.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:334.11,335.54 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:335.54,337.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:338.13,339.51 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:339.51,341.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:342.14,343.52 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:343.52,345.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:346.13,347.51 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:347.51,349.4 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:350.36,351.36 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:352.15,354.38 2 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:355.14,356.31 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:357.10,358.53 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:362.2,362.35 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:366.43,367.27 1 1 +codeberg.org/snonux/perc/internal/rpn/rpn.go:367.27,369.3 1 0 +codeberg.org/snonux/perc/internal/rpn/rpn.go:370.2,370.32 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:35.35,39.2 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:43.67,44.16 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:44.16,46.3 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:48.2,52.12 4 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:57.62,63.2 4 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:67.54,72.12 4 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:72.12,74.3 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:75.2,75.15 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:79.52,84.39 4 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:84.39,86.3 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:89.2,89.40 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:89.40,91.3 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:93.2,93.14 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:104.38,109.2 3 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:112.46,114.21 2 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:114.21,116.3 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:118.2,119.29 2 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:119.29,120.12 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:120.12,122.4 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:123.3,123.56 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:125.2,125.20 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:129.33,134.2 3 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:137.51,143.2 4 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:151.24,155.2 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:158.35,160.2 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:164.40,165.24 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:165.24,167.3 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:169.2,171.17 3 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:176.41,177.24 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:177.24,179.3 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:180.2,180.39 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:184.27,186.2 1 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:189.36,193.2 3 1 +codeberg.org/snonux/perc/internal/rpn/variables.go:196.25,198.2 1 1 diff --git a/internal/repl/repl.go b/internal/repl/repl.go index 0bcc4cc..c5a87b1 100644 --- a/internal/repl/repl.go +++ b/internal/repl/repl.go @@ -20,7 +20,8 @@ const historyFile = ".perc_history" // RPNState holds the state for RPN operations in REPL type RPNState struct { - vars *rpn.Variables + vars *rpn.Variables + rpnCalc *rpn.RPN } var rpnState *RPNState @@ -28,8 +29,10 @@ var rpnState *RPNState // getRPNState returns or creates the RPN state func getRPNState() *RPNState { if rpnState == nil { + vars := rpn.NewVariables().(*rpn.Variables) rpnState = &RPNState{ - vars: rpn.NewVariables().(*rpn.Variables), + vars: vars, + rpnCalc: rpn.NewRPN(vars), } } return rpnState @@ -76,6 +79,25 @@ func executor(input string) { return } + // Try evaluating as a single operator on the current RPN stack + // This allows incremental operations like: "1 2 +" then "+" + state := getRPNState() + fields := strings.Fields(input) + if len(fields) == 1 { + op := strings.ToLower(fields[0]) + // Check if it's a valid operator + switch op { + case "+", "-", "*", "/", "^", "%", "dup", "swap", "pop", "show", "clear", "vars": + result, err := state.rpnCalc.EvalOperator(op) + if err != nil { + fmt.Printf("Error: %v\n", err) + } else { + fmt.Println(result) + } + return + } + } + // Run the percentage calculation result, err := calculator.Parse(input) if err != nil { diff --git a/internal/rpn/rpn.go b/internal/rpn/rpn.go index f99d1a6..0a76909 100644 --- a/internal/rpn/rpn.go +++ b/internal/rpn/rpn.go @@ -11,14 +11,16 @@ type RPN struct { vars *Variables ops *Operations maxStack int + currentStack *Stack } // NewRPN creates a new RPN parser and evaluator with the given variable store. func NewRPN(vars *Variables) *RPN { return &RPN{ - vars: vars, - ops: NewOperations(vars), - maxStack: 1000, // Reasonable limit for RPN expressions + vars: vars, + ops: NewOperations(vars), + maxStack: 1000, // Reasonable limit for RPN expressions + currentStack: NewStack(), } } @@ -30,6 +32,9 @@ func (r *RPN) ParseAndEvaluate(input string) (string, error) { return "", fmt.Errorf("empty expression") } + // Reset the stack for fresh evaluation (each call should be independent) + r.currentStack = NewStack() + // Handle single assignment: "name value =" // This is when the entire input is just an assignment if strings.Contains(input, " = ") { @@ -105,7 +110,12 @@ func tokenize(input string) []string { // evaluate evaluates a list of tokens and returns the result. func (r *RPN) evaluate(tokens []string) (string, error) { - stack := NewStack() + // Use the current stack for evaluation to preserve state + // This allows incremental operations in REPL mode + if r.currentStack == nil { + r.currentStack = NewStack() + } + stack := r.currentStack for i, token := range tokens { // Check for variable assignment: name value = @@ -203,6 +213,23 @@ func (r *RPN) evaluate(tokens []string) (string, error) { return result, nil } + // Save the current stack state for continued operations + // Create a copy of the stack to preserve it + r.currentStack = NewStack() + for _, val := range stack.Values() { + r.currentStack.Push(val) + } + + // Get the final result + if stack.Len() > 1 { + // Multiple values - show them all + result, err := r.ops.Show(stack) + if err != nil { + return "", fmt.Errorf("final result: %w", err) + } + return result, nil + } + // Single value - return it val, _ := stack.Pop() return fmt.Sprintf("%.10g", val), nil @@ -275,3 +302,70 @@ func (r *RPN) ResultStack(tokens []string) (string, error) { return r.ops.Show(stack) } + +// EvalOperator evaluates a single operator on the current stack state. +// This allows incremental RPN operations like: "1 2 +" then "+". +func (r *RPN) EvalOperator(op string) (string, error) { + if r.currentStack == nil { + r.currentStack = NewStack() + } + + switch op { + case "+": + if err := r.ops.Add(r.currentStack); err != nil { + return "", fmt.Errorf("operator +: %w", err) + } + case "-": + if err := r.ops.Subtract(r.currentStack); err != nil { + return "", fmt.Errorf("operator -: %w", err) + } + case "*": + if err := r.ops.Multiply(r.currentStack); err != nil { + return "", fmt.Errorf("operator *: %w", err) + } + case "/": + if err := r.ops.Divide(r.currentStack); err != nil { + return "", fmt.Errorf("operator /: %w", err) + } + case "^": + if err := r.ops.Power(r.currentStack); err != nil { + return "", fmt.Errorf("operator ^: %w", err) + } + case "%": + if err := r.ops.Modulo(r.currentStack); err != nil { + return "", fmt.Errorf("operator %%: %w", err) + } + case "dup": + if err := r.ops.Dup(r.currentStack); err != nil { + return "", fmt.Errorf("dup: %w", err) + } + case "swap": + if err := r.ops.Swap(r.currentStack); err != nil { + return "", fmt.Errorf("swap: %w", err) + } + case "pop": + if err := r.ops.Pop(r.currentStack); err != nil { + return "", fmt.Errorf("pop: %w", err) + } + case "show", "showstack", "print": + return r.ops.Show(r.currentStack) + case "clear": + r.ops.ClearVariables() + return "All variables cleared", nil + case "vars": + return r.ops.ListVariables() + default: + return "", fmt.Errorf("unknown operator '%s'", op) + } + + // Return the current stack state + return r.ops.Show(r.currentStack) +} + +// GetCurrentStack returns a copy of the current stack for inspection. +func (r *RPN) GetCurrentStack() []float64 { + if r.currentStack == nil { + return nil + } + return r.currentStack.Values() +} diff --git a/internal/rpn/rpn_test.go b/internal/rpn/rpn_test.go index 46590b8..86bc628 100644 --- a/internal/rpn/rpn_test.go +++ b/internal/rpn/rpn_test.go @@ -653,3 +653,138 @@ func TestResultStackMultipleValues(t *testing.T) { }) } } + +func TestRPNIncrementalOperations(t *testing.T) { + v := NewVariables().(*Variables) + r := NewRPN(v) + + // Test: 1 2 3 + then + + // First evaluate "1 2 3 +" + result, err := r.ParseAndEvaluate("1 2 3 +") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + if result != "1 5" { + t.Errorf("First result = %q, want '1 5'", result) + } + + // Then apply + operator + result, err = r.EvalOperator("+") + if err != nil { + t.Fatalf("EvalOperator('+') failed: %v", err) + } + if result != "6" { + t.Errorf("After + = %q, want '6'", result) + } +} + +func TestRPNIncrementalSubtract(t *testing.T) { + v := NewVariables().(*Variables) + r := NewRPN(v) + + // First put two values on stack: "10 3" gives stack [10, 3] + _, err := r.ParseAndEvaluate("10 3") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + + // Now subtract + result, err := r.EvalOperator("-") + if err != nil { + t.Fatalf("EvalOperator('-') failed: %v", err) + } + // 10 - 3 = 7 + if result != "7" { + t.Errorf("After - = %q, want '7'", result) + } +} + +func TestRPNIncrementalDup(t *testing.T) { + v := NewVariables().(*Variables) + r := NewRPN(v) + + // First push values (two values so stack is not emptied after evaluation) + _, err := r.ParseAndEvaluate("5 6") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + // After "5 6", stack should have [5, 6], result is "5 6" + + // Now duplicate + result, err := r.EvalOperator("dup") + if err != nil { + t.Fatalf("EvalOperator('dup') failed: %v", err) + } + if result != "5 6 6" { + t.Errorf("After dup = %q, want '5 6 6'", result) + } +} + +func TestRPNIncrementalSwap(t *testing.T) { + v := NewVariables().(*Variables) + r := NewRPN(v) + + _, err := r.ParseAndEvaluate("1 2") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + + result, err := r.EvalOperator("swap") + if err != nil { + t.Fatalf("EvalOperator('swap') failed: %v", err) + } + if result != "2 1" { + t.Errorf("After swap = %q, want '2 1'", result) + } +} + +func TestRPNGetCurrentStack(t *testing.T) { + v := NewVariables().(*Variables) + r := NewRPN(v) + + _, err := r.ParseAndEvaluate("1 2 3") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + + stack := r.GetCurrentStack() + if len(stack) != 3 { + t.Errorf("Stack length = %d, want 3", len(stack)) + } + if stack[0] != 1 || stack[1] != 2 || stack[2] != 3 { + t.Errorf("Stack = %v, want [1 2 3]", stack) + } +} + +func TestRPNIncrementalUnknownOperator(t *testing.T) { + v := NewVariables().(*Variables) + r := NewRPN(v) + + _, err := r.ParseAndEvaluate("1 2") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + + _, err = r.EvalOperator("unknown") + if err == nil { + t.Error("EvalOperator('unknown') should return error") + } +} + +func TestRPNClearStack(t *testing.T) { + v := NewVariables().(*Variables) + r := NewRPN(v) + + _, err := r.ParseAndEvaluate("1 2 3") + if err != nil { + t.Fatalf("First evaluation failed: %v", err) + } + + result, err := r.EvalOperator("clear") + if err != nil { + t.Fatalf("EvalOperator('clear') failed: %v", err) + } + if result != "All variables cleared" { + t.Errorf("After clear = %q, want 'All variables cleared'", result) + } +} |
