11import { StmtNS , ExprNS } from '../ast-types' ;
22import { PyContext } from './py_context' ;
33import { PyControl , PyControlItem } from './py_control' ;
4- import { PyNode , Instr , InstrType , UnOpInstr , BinOpInstr } from './py_types' ;
4+ import { PyNode , Instr , InstrType , UnOpInstr , BinOpInstr , BoolOpInstr } from './py_types' ;
55import { Stash , Value , ErrorValue } from './stash' ;
66import { IOptions } from '..' ;
77import * as instr from './py_instrCreator' ;
@@ -137,6 +137,32 @@ const pyCmdEvaluators: { [type: string]: CmdEvaluator } = {
137137 control . push ( binary . left ) ;
138138 } ,
139139
140+ 'BoolOp' : ( command , context , control , stash , isPrelude ) => {
141+ const boolOp = command as ExprNS . BoolOp ;
142+ control . push ( instr . boolOpInstr ( boolOp . operator . type , boolOp ) ) ;
143+ control . push ( boolOp . right ) ;
144+ control . push ( boolOp . left ) ;
145+ } ,
146+
147+ 'None' : ( command , context , control , stash , isPrelude ) => {
148+ stash . push ( { type : 'undefined' } ) ;
149+ } ,
150+
151+ 'Variable' : ( command , context , control , stash , isPrelude ) => {
152+ const variable = command as ExprNS . Variable ;
153+ const name = variable . name . lexeme ;
154+ // For now, we only handle built in constants.
155+ // In a future commit, we will look up variables in the environment.
156+ if ( name === 'True' ) {
157+ stash . push ( { type : 'bool' , value : true } ) ;
158+ } else if ( name === 'False' ) {
159+ stash . push ( { type : 'bool' , value : false } ) ;
160+ } else {
161+ // Throw an error for undefined variables for now
162+ throw new Error ( `NameError: name '${ name } ' is not defined` ) ;
163+ }
164+ } ,
165+
140166 /**
141167 * Instruction Handlers
142168 */
@@ -170,4 +196,50 @@ const pyCmdEvaluators: { [type: string]: CmdEvaluator } = {
170196 stash . push ( result ) ;
171197 }
172198 } ,
199+
200+ [ InstrType . BOOL_OP ] : function ( command : PyControlItem , context : PyContext , control : PyControl , stash : Stash , isPrelude :
201+ boolean ) {
202+ const instr = command as BoolOpInstr ;
203+ const rightValue = stash . pop ( ) ;
204+ const leftValue = stash . pop ( ) ;
205+
206+ if ( ! leftValue || ! rightValue ) {
207+ throw new Error ( "RuntimeError: Boolean operation requires two operands." ) ;
208+ }
209+
210+ // Implement Python's short-circuiting logic
211+ if ( instr . symbol === TokenType . OR ) {
212+ // If left is truthy, return left. Otherwise, return right.
213+ let isLeftTruthy = false ;
214+ if ( leftValue . type === 'bool' ) isLeftTruthy = leftValue . value ;
215+ else if ( leftValue . type === 'bigint' ) isLeftTruthy = leftValue . value !== 0n ;
216+ else if ( leftValue . type === 'number' ) isLeftTruthy = leftValue . value !== 0 ;
217+ else if ( leftValue . type === 'string' ) isLeftTruthy = leftValue . value !== '' ;
218+ else if ( leftValue . type === 'undefined' ) isLeftTruthy = false ;
219+ else isLeftTruthy = true ; // Other types are generally truthy
220+
221+ if ( isLeftTruthy ) {
222+ stash . push ( leftValue ) ;
223+ } else {
224+ stash . push ( rightValue ) ;
225+ }
226+ } else if ( instr . symbol === TokenType . AND ) {
227+ // If left is falsy, return left. Otherwise, return right.
228+ let isLeftFalsy = false ;
229+ if ( leftValue . type === 'bool' ) isLeftFalsy = ! leftValue . value ;
230+ else if ( leftValue . type === 'bigint' ) isLeftFalsy = leftValue . value === 0n ;
231+ else if ( leftValue . type === 'number' ) isLeftFalsy = leftValue . value === 0 ;
232+ else if ( leftValue . type === 'string' ) isLeftFalsy = leftValue . value === '' ;
233+ else if ( leftValue . type === 'undefined' ) isLeftFalsy = true ;
234+ else isLeftFalsy = false ; // Other types are generally truthy
235+
236+ if ( isLeftFalsy ) {
237+ stash . push ( leftValue ) ;
238+ } else {
239+ stash . push ( rightValue ) ;
240+ }
241+ } else {
242+ throw new Error ( `Unsupported boolean operator: ${ instr . symbol } ` ) ;
243+ }
244+ } ,
173245} ;
0 commit comments