Format strings ============== Safer alternatives to varargs / format strings: 1. Have an "mixed-element-type-array" type, with runtime type info. 2. Have an "to-string-element-array" type, where the compiler automatically inserts conversions to string for each element. (Or perhaps an "auto-converting-string" type) 3. Require multiple calls: system.out.param_int(123) system.out.param_bool(true) system.out.param_string("abcdef") system.out.writeln("Hello string={3}, int={1,right,width=4,pad= }, bool={2,false=No,true=Yes}") - How to handle lifetimes? - A possible solution: { FormatParams p = .new(arena) p.param_int(123) p.param_bool(true) p.param_string("abcdef") system.out.writefmt("Hello string={3}, int={1,right,width=4,pad= }, bool={2,false=No,true=Yes}", p) } - Underlying API types and functions: type FormatParams func .new(arena) -> FormatParams func FormatParams.param_...(...) ... = { int,uint,int64,uint64,fileoffs,size,bool,string } type Formatter func .new(arena, size buffsize) -> Formatter func Formatter.start(string fmt, ref FormatParams params) func Formatter.next(ref writeonly byte[len] buff, size len) -> ?size where return <= len # Someting like this would be cool #func Formatter.next(out byte[len] buff, size len) -> ?size # where only buff[0..return] is initialized Language changes: - allow ? with types where a none value can be reserved? ?byte, ?uint8..16, ?int16, ?size, ?fileoffs - array initialization constraints? (probably hard) - Type-checked format strings? ---------------------------- C-like syntax: func greeting(arena Stream out, string name, int number) { out.write_fmt(%"Hello %s, the number is %02d") out.write_fmt(%"Hello %s(name), the number is %02d(number)") out.write_fmt(%"Hello %s[name], the number is %02d[number]") out.write_fmt(%"Hello %s{name}, the number is %02d{number}") out.write_fmt(%"Hello %{s:name}, the number is %{02d:number}") out.write_fmt("Hello \{s:name}, the number is \{02d:number}") } Pros: + Familar to C programmers + Could be used in places where strings are expected: %"..." where string is expected: gets formatted first. %"..." where a format string is expected: passed as format string. Cons: - Might need to mix lowercase and uppercase: Future C standards can add more lowercase letters, so any SLUL extensions have to use (or start with) an uppercase letter. - Incompatible syntax with C. To be compatible with C: Extensible / general purpose formatting --------------------------------------- string s = .format("Hello \{s:name}, this is \{d,2,.right:number}") string s = .format("Hello \{s:name}, this is \{d(2,.right):number}") string s = .format("Hello \{.s(name)}, this is \{.d(number, 2, .right)}") string s = .format("Hello \.s(name), this is \.d(number, 2, .right)") func .format(Format f) -> string func .s(string s) -> FormatItem func .d(int n, int num_digits, Align align) -> FormatItem string s = .format("Hello \.s(name), this is \.rnum(number, 2)") func .rnum(int n, int num_digits) -> FormatItem { return .d(n, num_digits, .right) } Including the variable name --------------------------- int id = 123 string name = "Jane" string msg = .format("Error. \={id}, \={name}") Would result in: Error. id=123, name="Jane" Out-of-band placeholders? ------------------------- Typically, the placeholders for values are in-band: "Hello %s" "Hello {}" "Hello {0}" Out-of-band placeholders have two advantages: * Possibly better performance since the string does not have to be scanned. * Can avoid some security issues where format strings come from untrusted sources (this should not cause memory errors in SLUL, but it could still cause other problems) And disadvantages: * It can create surprises when two strings can have the exact same byte representation, but still behave in different ways when used as format strings. * Translation tools/libraries would have to support the out-of-band placeholders.