import * as C from '@france-ioi/persistent-c' ; import { StepperContext } from "../api" ; const re = { text : /^[^%]+/ , percent : /^%%/ , format_specifier : /^%([-+ #0]+)?([1-9]\d*)?(?:\.([1-9]\d*|\*))?(h|hh|l|ll|z|t)?([diuoxXfFeEcsp])/ , sign : /^[\+\-]/ } ; const formatCache = new Map ( ) ; export function * printfBuiltin ( stepperContext , fmtRef , ... args ) { const { programState } = stepperContext . state ; const fmt = C . readString ( programState . memory , fmtRef ) ; if ( ! formatCache . has ( fmt ) ) { formatCache . set ( fmt , parseFormat ( fmt ) ) ; } const str = applyFormat ( programState , formatCache . get ( fmt ) , args ) ; yield [ 'write' , str ] ; const result = new C . IntegralValue ( C . builtinTypes [ 'int' ] , str . length ) ; yield [ 'result' , result ] ; } function parseFormat ( fmt ) { const ops = [ ] ; while ( fmt . length > 0 ) { let match ; if ( ( match = re . text . exec ( fmt ) ) !== null ) { ops . push ( { text : match [ 0 ] } ) ; } else if ( ( match = re . percent . exec ( fmt ) ) !== null ) { ops . push ( { text : "%" } ) ; } else if ( ( match = re . format_specifier . exec ( fmt ) ) !== null ) { const flags = match [ 1 ] || '' ; const zeroPad = /0/ . test ( flags ) ; const leftJustify = /-/ . test ( flags ) ; const signPositives = /\+/ . test ( flags ) ; const spaceSign = / / . test ( flags ) ; const decorate = /#/ . test ( flags ) ; const width = match [ 2 ] ; const precision = match [ 3 ] ; const length = match [ 4 ] ; const specifier = match [ 5 ] ; ops . push ( { zeroPad , leftJustify , signPositives , spaceSign , width , precision , length , specifier } ) ; } else { throw new SyntaxError ( `printf: unexpected format specifier "${ fmt }"` ) ; } fmt = fmt . substring ( match [ 0 ] . length ) ; } return ops ; } function applyFormat ( programState , ops , args ) { const output = [ ] ; let cursor = 0 ; for ( let i = 0 ; i < ops . length ; i ++ ) { const op = ops [ i ] ; if ( 'text' in op ) { output . push ( op . text ) ; continue ; } let arg = args [ cursor ++ ] ; if ( op . specifier === 'c' ) { output . push ( String . fromCharCode ( arg . toInteger ( ) ) ) ; continue ; } let precision = op . precision ; let prefix = '' , suffix = '' , sign = '' ; if ( op . specifier === 's' ) { arg = C . readString ( programState . memory , arg , op . precision ) ; if ( precision !== null ) { arg = arg . substring ( 0 , precision ) ; } output . push ( justify ( op , arg , '' , '' , '' ) ) ; continue ; } switch ( op . specifier ) { case "d" : case "i" : sign = ( arg . number >= 0 ) ? "+" : "-" ; arg = zeroFill ( arg . number . toString ( ) , precision ) ; arg = arg . replace ( re . sign , "" ) ; break ; case "u" : arg = arg . number >>> 0 ; arg = zeroFill ( arg . toString ( ) , precision ) ; break ; case "o" : arg = arg . number >>> 0 ; if ( op . decorate && arg !== 0 ) { prefix = '0' ; } arg = zeroFill ( arg . toString ( 8 ) , precision ) ; break ; case "x" : case 'X' : arg = arg . number >>> 0 ; if ( op . decorate && arg !== 0 ) { prefix = '0x' ; } arg = zeroFill ( arg . toString ( 16 ) , precision ) ; break ; case "f" : case 'F' : case "e" : case 'E' : sign = ( arg . number >= 0 ) ? "+" : "-" ; if ( /[fF]/ . test ( op . specifier ) ) { arg = arg . number . toFixed ( precision || 6 ) ; } else { arg = arg . number . toExponential ( precision || 6 ) ; } arg = arg . replace ( re . sign , "" ) ; if ( op . decorate ) { suffix = '.' ; } break ; case "p" : arg = ( arg . address >>> 0 ) . toString ( 16 ) ; break ; } arg = arg . replace ( re . sign , "" ) ; arg = justify ( op , arg , sign , prefix , suffix ) ; if ( /[XFE]/ . test ( op ) ) { arg = arg . toUpperCase ( ) ; } output . push ( arg ) ; } return output . join ( '' ) ; } function replicate ( count , text ) { return Array ( count ) . fill ( text ) . join ( '' ) ; } function zeroFill ( value , precision ) { if ( value . length < precision ) { return replicate ( precision - value . length , '0' ) + value ; } if ( precision === 0 && value === '0' ) { return '' ; } return value ; } function justify ( op , arg , sign , prefix , suffix ) { if ( sign === '+' && ! op . signPositives ) { sign = op . spaceSign ? ' ' : '' ; } let padding = '' ; const paddingLength = op . width - sign . length - prefix . length - arg . length - suffix . length ; if ( paddingLength > 0 ) { padding = replicate ( paddingLength , op . zeroPad ? '0' : ' ' ) ; } if ( op . leftJustify ) { return sign + prefix + arg + suffix + padding ; } if ( op . zeroPad ) { return sign + prefix + padding + arg + suffix ; } return padding + sign + prefix + arg + suffix ; }