@@ -11,11 +11,11 @@ import { PyClosure } from './py_closure';
1111import { PyContext } from './py_context' ;
1212import { PyControl , PyControlItem } from './py_control' ;
1313import { createEnvironment , currentEnvironment , pushEnvironment , popEnvironment } from './py_environment' ;
14- import { PyNode , Instr , InstrType , UnOpInstr , BinOpInstr , BoolOpInstr , AssmtInstr , AppInstr } from './py_types' ;
14+ import { PyNode , Instr , InstrType , UnOpInstr , BinOpInstr , BoolOpInstr , AssmtInstr , AppInstr , BranchInstr } from './py_types' ;
1515import { Stash , Value , ErrorValue } from './stash' ;
1616import { IOptions } from '..' ;
1717import * as instrCreator from './py_instrCreator' ;
18- import { evaluateUnaryExpression , evaluateBinaryExpression , evaluateBoolExpression } from './py_operators' ;
18+ import { evaluateUnaryExpression , evaluateBinaryExpression , evaluateBoolExpression , isFalsy } from './py_operators' ;
1919import { TokenType } from '../tokens' ;
2020import { Token } from '../tokenizer' ;
2121import { Result , Finished , CSEBreak , Representation } from '../types' ;
@@ -66,9 +66,9 @@ export function PyCSEResultPromise(context: PyContext, value: Value): Promise<Re
6666 */
6767export function PyEvaluate ( code : string , program : StmtNS . Stmt , context : PyContext , options : IOptions ) : Value {
6868 try {
69- context . control = new PyControl ( program ) ;
7069 context . runtime . isRunning = true ;
71-
70+ context . control = new PyControl ( program ) ;
71+
7272 const result = pyRunCSEMachine (
7373 code ,
7474 context ,
@@ -78,7 +78,7 @@ export function PyEvaluate(code: string, program: StmtNS.Stmt, context: PyContex
7878 options . stepLimit ,
7979 options . isPrelude || false ,
8080 ) ;
81- return result ;
81+ return context . output ? { type : "string" , value : context . output } : { type : 'undefined' } ;
8282 } catch ( error : any ) {
8383 return { type : 'error' , message : error . message } ;
8484 } finally {
@@ -98,7 +98,7 @@ export function PyEvaluate(code: string, program: StmtNS.Stmt, context: PyContex
9898 * @param isPrelude Whether the program is the prelude.
9999 * @returns The top value of the stash after execution.
100100 */
101- function pyRunCSEMachine (
101+ export function pyRunCSEMachine (
102102 code : string ,
103103 context : PyContext ,
104104 control : PyControl ,
@@ -397,6 +397,37 @@ const pyCmdEvaluators: { [type: string]: CmdEvaluator } = {
397397 }
398398 } ,
399399
400+ 'If' : ( code , command , context , control , stash , isPrelude ) => {
401+ const ifNode = command as StmtNS . If ;
402+
403+ // create branch instruction, wrap statement arrays in 'StatementSequence' objects
404+ const branch = instrCreator . branchInstr (
405+ { type : 'StatementSequence' , body : ifNode . body } ,
406+ ifNode . elseBlock
407+ ? ( Array . isArray ( ifNode . elseBlock )
408+ // 'else' block
409+ ? { type : 'StatementSequence' , body : ifNode . elseBlock }
410+ // 'elif' block
411+ : ifNode . elseBlock )
412+ // 'else' block dont exist
413+ : null ,
414+ ifNode
415+ ) ;
416+ control . push ( branch ) ;
417+ control . push ( ifNode . condition ) ;
418+ } ,
419+
420+ 'Ternary' : ( code , command , context , control , stash , isPrelude ) => {
421+ const ternaryNode = command as ExprNS . Ternary ;
422+ const branch = instrCreator . branchInstr (
423+ ternaryNode . consequent ,
424+ ternaryNode . alternative ,
425+ ternaryNode
426+ ) ;
427+ control . push ( branch ) ;
428+ control . push ( ternaryNode . predicate ) ;
429+ } ,
430+
400431 /**
401432 * Instruction Handlers
402433 */
@@ -471,31 +502,39 @@ const pyCmdEvaluators: { [type: string]: CmdEvaluator } = {
471502 args . unshift ( stash . pop ( ) ) ;
472503 }
473504
474- // pop closure from stash
475- const closure = stash . pop ( ) as PyClosure ;
476-
477- // push reset and implicit return for cleanup at end of function
478- control . push ( instrCreator . resetInstr ( instr . srcNode ) ) ;
505+ // pop callable from stash
506+ const callable = stash . pop ( ) ;
479507
480- // Only push endOfFunctionBodyInstr for functionDef
481- if ( closure . node . constructor . name === 'FunctionDef' ) {
482- control . push ( instrCreator . endOfFunctionBodyInstr ( instr . srcNode ) ) ;
483- }
508+ if ( callable instanceof PyClosure ) {
509+ // User-defined function
510+ const closure = callable as PyClosure ;
511+ // push reset and implicit return for cleanup at end of function
512+ control . push ( instrCreator . resetInstr ( instr . srcNode ) ) ;
484513
485- // create new function environment
486- const newEnv = createEnvironment ( context , closure , args , instr . srcNode as ExprNS . Call ) ;
487- pushEnvironment ( context , newEnv ) ;
514+ // Only push endOfFunctionBodyInstr for functionDef
515+ if ( closure . node . constructor . name === 'FunctionDef' ) {
516+ control . push ( instrCreator . endOfFunctionBodyInstr ( instr . srcNode ) ) ;
517+ }
488518
489- // push function body onto control stack
490- const closureNode = closure . node ;
491- if ( closureNode . constructor . name === 'FunctionDef' ) {
492- // 'def' has a body of statements (an array)
493- const bodyStmts = ( closureNode as StmtNS . FunctionDef ) . body . slice ( ) . reverse ( ) ;
494- control . push ( ...bodyStmts ) ;
519+ // create new function environment
520+ const newEnv = createEnvironment ( context , closure , args , instr . srcNode as ExprNS . Call ) ;
521+ pushEnvironment ( context , newEnv ) ;
522+
523+ // push function body onto control stack
524+ const closureNode = closure . node ;
525+ if ( closureNode . constructor . name === 'FunctionDef' ) {
526+ // 'def' has a body of statements (an array)
527+ const bodyStmts = ( closureNode as StmtNS . FunctionDef ) . body . slice ( ) . reverse ( ) ;
528+ control . push ( ...bodyStmts ) ;
529+ } else {
530+ // 'lambda' has a body with a single expression
531+ const bodyExpr = ( closureNode as ExprNS . Lambda ) . body ;
532+ control . push ( bodyExpr ) ;
533+ }
495534 } else {
496- // 'lambda' has a body with a single expression
497- const bodyExpr = ( closureNode as ExprNS . Lambda ) . body ;
498- control . push ( bodyExpr ) ;
535+ // Built-in function from stdlib / constants
536+ const result = ( callable as any ) ( context , ... args ) ;
537+ stash . push ( result ) ;
499538 }
500539 } ,
501540
@@ -508,4 +547,35 @@ const pyCmdEvaluators: { [type: string]: CmdEvaluator } = {
508547 stash . push ( { type : 'undefined' } ) ;
509548 } ,
510549
550+ [ InstrType . BRANCH ] : ( code , command , context , control , stash , isPrelude ) => {
551+ const instr = command as BranchInstr ;
552+ const condition = stash . pop ( ) ;
553+
554+ if ( ! isFalsy ( condition ) ) {
555+ // Condition is truthy, execute the consequent
556+ const consequent = instr . consequent ;
557+ if ( consequent && 'type' in consequent && consequent . type === 'StatementSequence' ) {
558+ control . push ( ...( consequent as any ) . body . slice ( ) . reverse ( ) ) ;
559+ } else if ( consequent ) {
560+ // consequent of ternary or single statement
561+ control . push ( consequent ) ;
562+ }
563+ } else if ( instr . alternate ) {
564+ // Condition is falsy, execute the alternate
565+ const alternate = instr . alternate ;
566+ if ( alternate && 'type' in alternate && alternate . type === 'StatementSequence' ) {
567+ // 'else' block
568+ control . push ( ...( alternate as any ) . body . slice ( ) . reverse ( ) ) ;
569+ } else if ( alternate ) {
570+ // 'elif' or ternary alternative
571+ control . push ( alternate ) ;
572+ }
573+ }
574+ // If condition is falsy and there's no alternate, do nothing
575+ } ,
576+
577+ [ InstrType . POP ] : ( code , command , context , control , stash , isPrelude ) => {
578+ stash . pop ( ) ;
579+ } ,
580+
511581} ;
0 commit comments