From e1219ea0c1a60f8492763496f54b73db23299316 Mon Sep 17 00:00:00 2001 From: Aaron Sinclair Date: Sat, 15 Nov 2025 12:18:16 -0800 Subject: [PATCH] Implement binary operations (BinOp) support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Parser changes (parser.py):** - Added ast.BinOp handling in _transform_expr() - Emits: {type: "binop", left: ..., op: "Add|Sub|Mult|Div|Mod", right: ...} **Codegen changes (llvm_codegen_signals.py):** - Added BinOp handling in _emit_expr() - Maps Python ops to LLVM instructions: - Add → add i32 - Sub → sub i32 - Mult → mul i32 - Div → sdiv i32 (signed division) - Mod → srem i32 (signed remainder) **Result:** Before: count = count + 1 → store i32 0 (WRONG!) After: count = count + 1 → load, add i32 1, store ✓ **Still TODO:** The counter buttons now UPDATE the signal correctly, but the display doesn't update because f-string text nodes aren't reactive yet. We need reactive text nodes (similar to reactive attributes). All 55 tests passing ✓ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- examples/tabs/.Tabs.py.swp | Bin 16384 -> 0 bytes examples/textinput/TextInput.ll | 98 +++++++++--------- examples/textinput/TextInput.wasm | Bin 1398 -> 1424 bytes .../llvm_codegen_signals.cpython-313.pyc | Bin 61869 -> 62431 bytes ritz/__pycache__/parser.cpython-313.pyc | Bin 17853 -> 18187 bytes ritz/llvm_codegen_signals.py | 19 ++++ ritz/parser.py | 8 ++ 7 files changed, 78 insertions(+), 47 deletions(-) delete mode 100644 examples/tabs/.Tabs.py.swp diff --git a/examples/tabs/.Tabs.py.swp b/examples/tabs/.Tabs.py.swp deleted file mode 100644 index 9b5d79c645669c83c22b5cc542b2ec37f41ce343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI3TWlOx8OJAu1{#vIBnXhWeOW)muDss0Ly)?XMO}LxM`Rs0b`pVFS!ZX@?jAZj zvzp7sZn#(F0fZ7R4-i#xRVt*aYDERTs1G2~sud4Ng$L9MTBPv63o7RF0;wwg=ggV) z%&vFWu`Noa8Rh4(cV^D{&Ud~!=l`9vDd*3fEYTff;|7k~4C6zud{n&t*z=L3#1vuoVNj{*06MHQH<@K(;|Ep7=f=L-M+9A9CLrp3LS&&pnI2|vb~s{_hP zFQ*Veb)sRvRIq#j5;ka{5XKJ*6HcfbiS3vLGo!2$4>1BUT4@E|w}-rR2(FN2pr9pu11a0__+1BT&(Q{Z!86kOkD z7=HvWf=j>xd2kTCdW&H^10De10AB>h!6ESK`*97dfOFte;FDl4`0e}9PT+yN!6(4= zy@v5Dcm`Yr0-OMc!6EQLuowL1X2ZA)J`Ik6kAXeF0I$6l?E-7yD!2%;U=;idiz%;w zr@#YX7JMA+1AoRs%um3Vz(sHm7z2Mt|HwXiVUqT#?JZdE9=zV_WmX~A`>!r~cY zvwJTHzfN}rzVFz>j%`}Pyl>KCjdFz*$m9MpzLzmPJ~C41dRRSpN(`G8^StHB3~nT5 z`l7`%9e0Z=;o`EeD}03}CuzK3bd?sF*0%f7!!+kKc3^aVi$-@^%Po(0xAvX0Z;MXN zZ`iDQ=ByR(nQVjGLA<3ygzwsr{FJg3SUF3O=evRF2QDX7K-ib~l=<9Cc9`y@yEqGc zQ4K6Iokr8~gfARBd>Dn7WZBHF$ch%2&BL2)Zu{OwB&wPg+Ln4j*%v-S6%mvO5nZFS zd0N&bi=i3ba5{>b8z0uer%Ij?t#;WyzFU+v9ouKZ=5C?}T#BNTTFeT#s=LCg?KePu z8W|P8-4ivNS;P4eBDmCL=6!UbF3h@&coLrOyaMHEQpNKqjpH}{Mvl@1{#fL`FYKD9 zD$QgvB^W3l_uw&YE&9lD%{oUH$=(S2P5QP8$9iLU$dJ}9;h|y26>tudeKDCIDM-6y z<#l|1zbwxRO=xk~7bdfeJ5}dNhhp@iK8NmulQx_oD$+I5?2QiSd{p;s$6yfM)zv?` zysT_eJW`_vOy+S~?ud)%VszFv7FU|w6^L#$mu5MWQSB9IMtH1j@yd2>?x1uVCN?l*O@->>Ow3u!3N;^0P6h3z>iu=gM=)65YPi)Q4Q;7Xlgb{puy6I<~ zz#m}eLN7BTb9D#KFrVQ~w=9ks#j$t2>({z*hwi+@%iP-wn+Y#% z@9;#Yj!|#4R%}{)9gUl<=bWPi-4$!JR9+J#)eR+XKRVhy?6OG4 zLJTEBW)nFS#brVgB_B$NA}K!fco2ynHSC7sCz|M#mC!NqW_eU~+$e;hh9G@vQr&C_ zp6G5Rd>}LuTBhnc4U%6n9}7J}0>aVt>2_qY5KLK`vMjQknlK51>-^MWX_Okm77Yws z;S;!L+OaE2*W$zJ#YIvt92A(eEUu7oYe^0>IVDr%)vz<9nn7{!zgdS`QYA+EHLjY6 z(FCcOMt!uD7xjY9 zfKulsdL_fX`%3F9rP!j%C%{5y)azrt4cwY{bscI~;PdkR=({x_EmY-h zCsK9Od$oEfs#?XV(di5ojDr?PiAqk~dfh2rdAOsaHUy9ysx`09WVD?Z_MS?ka1Yn<`p;x4yasF? vcCB8u9iH4ejJHJIvZYDq x`p3l1#K6tL;JAZZ1IXY4F%%fgfJ{LU6C&)$n1vydy;+6HiII_eb2>9W697#L8+`x( delta 93 zcmbQh{f%qFYF2hO1{UUt8#hXG3KXU0rKA?chon}Nc;*$9mN0O!p_=Wo%b` n%EZRRz|FznxPw~*$lwAo6d254Oh<;zc1%u;j9i-=nfaLj$pjXr diff --git a/ritz/__pycache__/llvm_codegen_signals.cpython-313.pyc b/ritz/__pycache__/llvm_codegen_signals.cpython-313.pyc index dca0fc521d56c45e056418c08a89ad1cb5c5d373..c25a0c7f5f42ebc39b0a2b19a307597e7cac2a13 100644 GIT binary patch delta 530 zcmZ4cnEC#5X1>q7yj%=GaP7WC=ES=j`Gk)#$+2!$KE}o(7|IyT$O@DSR@U`T9c zn4EA@YI5FjK1RODi6

RX~!aj3C_%40-%O<)R>wJb_@Fss=};58B?aocQVx{UpQ&RuN^AL#}F(Y$_FGRLir~Po|0zN*{pj?hSBpDYf@%j ze!(r)qRjM+l3UD)DJi#@i%XMkG3S=%++rzC$t(ksMX9-(tVIWbcHUwR21>H{mgWEz zy8tDbee+YQm=$a%dz?086q;OhI$z@!M^0*5Nqkvi&Mi)`u^?vA0g!I?oSd@U`22!m zAs}zELXX5`rZd`%k2dR_v0|FM<}A#v<>jNj~k{TB!0qRCzlk{MTSUiV-LGvl4j!B0vU8J|u* z@$?>J`{d=%Oc-BpzVobxnQ_hJ$X88_A2;85b%Tj9VDr|uJDKEy7+J+q7yj%=Gu5>u&zyV4O0!@=-G5w9R)P zEn#Lnxw+|C2_xgB$={#fW2~8c?S%>Bjm_LIYnU15P40Np#CUHr_uCsxjJ}(nyx++r zZOSEkJ+3mA4?Sv8OS6mWv8XTvi%piW(PR{#>|~?MC^5Of=Arg2 z)}+k5`~ppuA|;>=MY}=7VjyvgwJ0+^qogPtBoF~4G!-Vt+V14L#p;xq=U)I;Q?!4w zu$?vIg3bPR>P(Xj98@RQ>S#``bP!|mWt=?0K~CCNlH;(Ih_4*i5g`>Ic}!c>SDEdk zyr{1R*JMQ#vCa1#j2IaYO%`&}VJzNk?_|lycx7{?Gc%K;C8G=D4CVP6Gc^{3%+&wD u0OGGm*hYChEM$rk*(>*47d|&`F7YKg^QAOfF*8%`Eomsj7 delta 189 zcmeC~W8B-#$oH9-mx}=iex8-c)H2z~7h=Z9u{qgnGb>ZD$mCZxnv9~8MQn8$#U?x1 zKIAD12dXTJ01}$All|;=PL{B@U<}zDXs^yR`LLrTqwVB-j&jo81{{a2M7&M8jtHp$ z$z$50-d1cUixh5-`h;7z&GGb&rFgd|lhcSC|ud^j1jS*XDH9t jn5nTKWTyTH1`vNm$_C>d%o{9L=6wP4KV(e);3f?K_wqQz diff --git a/ritz/llvm_codegen_signals.py b/ritz/llvm_codegen_signals.py index 5730b1c..fe7f42d 100644 --- a/ritz/llvm_codegen_signals.py +++ b/ritz/llvm_codegen_signals.py @@ -1093,6 +1093,25 @@ class SignalLLVMIRGenerator: result_var = self._new_var() self.lines.append(f" %{result_var} = select i1 %{cond_var}, i32 {true_val}, i32 {false_val}") return f"%{result_var}" + elif expr["type"] == "binop": + # Handle binary operations (e.g., count + 1, x - 5) + left_val = self._emit_expr(expr["left"]) + right_val = self._emit_expr(expr["right"]) + op = expr["op"] + + # Map Python operators to LLVM instructions + op_map = { + "Add": "add", + "Sub": "sub", + "Mult": "mul", + "Div": "sdiv", # signed division + "Mod": "srem", # signed remainder + } + + llvm_op = op_map.get(op, "add") + result_var = self._new_var() + self.lines.append(f" %{result_var} = {llvm_op} i32 {left_val}, {right_val}") + return f"%{result_var}" else: # Unknown expression type return "0" diff --git a/ritz/parser.py b/ritz/parser.py index df6590c..06c2135 100644 --- a/ritz/parser.py +++ b/ritz/parser.py @@ -356,6 +356,14 @@ def _transform_expr(expr: ast.expr | None) -> dict[str, Any]: "body": _transform_expr(expr.body), "orelse": _transform_expr(expr.orelse), } + elif isinstance(expr, ast.BinOp): + # Handle binary operations (e.g., count + 1, x - 5, a * b) + return { + "type": "binop", + "left": _transform_expr(expr.left), + "op": type(expr.op).__name__, # Add, Sub, Mult, Div, Mod, etc. + "right": _transform_expr(expr.right), + } else: return {"type": "unknown", "ast_type": type(expr).__name__}