How does string interpolation / string templates work? - vala

#lf_araujo asked in this question:
var dic = new dict of string, string
dic["z"] = "23"
dic["abc"] = "42"
dic["pi"] = "3.141"
for k in sorted_string_collection (dic.keys)
print (#"$k: $(dic[k])")
What is the function of # in print(# ... ) and lines_add(# ...)?
As this is applicable to both Genie and Vala, I thought it would be better suited as a stand-alone question.
The conceptual question is:
How does string interpolation work in Vala and Genie?

There are two options for string interpolation in Vala and Genie:
printf-style functions:
var name = "Jens Mühlenhoff";
var s = string.printf ("My name is %s, 2 + 2 is %d", name, 2 + 2);
This works using varargs, you have to pass multiple arguments with the correct types to the varargs function (in this case string.printf).
string templates:
var name = "Jens Mühlenhoff";
var s = #"My name is $name, 2 + 2 is $(2 + 2)";
This works using "compiler magic".
A template string starts with #" (rather then " which starts a normal string).
Expressions in the template string start with $ and are enclosed with (). The brackets are unneccesary when the expression doesn't contain white space like $name in the above example.
Expressions are evaluated before they are put into the string that results from the string template. For expressions that aren't of type string the compiler tries to call .to_string (), so you don't have to explicitly call it. In the $(2 + 2) example the expression 2 + 2 is evaluated to 4 and then 4.to_string () is called with will result in "4" which can then be put into the string template.
PS: I'm using Vala syntax here, just remove the ;s to convert to Genie.

Related

How to iterate over a compile-time seq in a manner that unrolls the loop?

I have a sequence of values that I know at compile-time, for example: const x: seq[string] = #["s1", "s2", "s3"]
I want to loop over that seq in a manner that keeps the variable a static string instead of a string as I intend to use these strings with macros later.
I can iterate on objects in such a manner using the fieldPairs iterator, but how can I do the same with just a seq?
A normal loop such as
for s in x:
echo s is static string
does not work, as s will be a string, which is not what I need.
The folks over at the nim forum were very helpful (here the thread).
The solution appears to be writing your own macro to do this. 2 solutions I managed to make work for me were from the users mratsim and a specialized version from hlaaftana
Hlaaftana's version:
This one unrolls the loop over the various values in the sequence. By that I mean, that the "iterating variable s" changes its value and is always the value of one of the entries of that compile-time seq x (or in this example a). In that way it functions basically like a normal for-in loop.
import macros
macro unrollSeq(x: static seq[string], name, body: untyped) =
result = newStmtList()
for a in x:
result.add(newBlockStmt(newStmtList(
newConstStmt(name, newLit(a)),
copy body
)))
const a = #["la", "le", "li", "lo", "lu"]
unrollSeq(a, s):
echo s is static
echo s
mratsim's version:
This one doesn't unroll a loop over the values, but over a range of indices.
You basically tell the staticFor macro over what range of values you want an unrolled for loop and it generates that for you. You can access the individual entries in the seq then with that index.
import std/macros
proc replaceNodes(ast: NimNode, what: NimNode, by: NimNode): NimNode =
# Replace "what" ident node by "by"
proc inspect(node: NimNode): NimNode =
case node.kind:
of {nnkIdent, nnkSym}:
if node.eqIdent(what):
return by
return node
of nnkEmpty:
return node
of nnkLiterals:
return node
else:
var rTree = node.kind.newTree()
for child in node:
rTree.add inspect(child)
return rTree
result = inspect(ast)
macro staticFor*(idx: untyped{nkIdent}, start, stopEx: static int, body: untyped): untyped =
result = newStmtList()
for i in start .. stopEx: # Slight modification here to make indexing behave more in line with the rest of nim-lang
result.add nnkBlockStmt.newTree(
ident("unrolledIter_" & $idx & $i),
body.replaceNodes(idx, newLit i)
)
staticFor(index, x.low, x.high):
echo index
echo x[index] is static string
Elegantbeefs version
Similar to Hlaaftana's version this unrolls the loop itself and provides you a value, not an index.
import std/[macros, typetraits]
proc replaceAll(body, name, wth: NimNode) =
for i, x in body:
if x.kind == nnkIdent and name.eqIdent x:
body[i] = wth
else:
x.replaceAll(name, wth)
template unrolledFor*(nameP, toUnroll, bodyP: untyped): untyped =
mixin
getType,
newTree,
NimNodeKind,
`[]`,
add,
newIdentDefs,
newEmptyNode,
newStmtList,
newLit,
replaceAll,
copyNimTree
macro myInnerMacro(name, body: untyped) {.gensym.} =
let typ = getType(typeof(toUnroll))
result = nnkBlockStmt.newTree(newEmptyNode(), newStmtList())
result[^1].add nnkVarSection.newTree(newIdentDefs(name, typ[^1]))
for x in toUnroll:
let myBody = body.copyNimTree()
myBody.replaceAll(name, newLit(x))
result[^1].add myBody
myInnerMacro(nameP, bodyP)
const x = #["la", "le", "Li"]
unrolledFor(value, x):
echo value is static
echo value
All of them are valid approaches.

lua tables - string representation

as a followup question to lua tables - allowed values and syntax:
I need a table that equates large numbers to strings. The catch seems to be that strings with punctuation are not allowed:
local Names = {
[7022003001] = fulsom jct, OH
[7022003002] = kennedy center, NY
}
but neither are quotes:
local Names = {
[7022003001] = "fulsom jct, OH"
[7022003002] = "kennedy center, NY"
}
I have even tried without any spaces:
local Names = {
[7022003001] = fulsomjctOH
[7022003002] = kennedycenterNY
}
When this module is loaded, wireshark complains "}" is expected to close "{" at line . How can I implement a table with a string that contains spaces and punctuation?
As per Lua Reference Manual - 3.1 - Lexical Conventions:
A short literal string can be delimited by matching single or double quotes, and can contain the (...) C-like escape sequences (...).
That means the short literal string in Lua is:
local foo = "I'm a string literal"
This matches your second example. The reason why it fails is because it lacks a separator between table members:
local Names = {
[7022003001] = "fulsom jct, OH",
[7022003002] = "kennedy center, NY"
}
You can also add a trailing separator after the last member.
The more detailed description of the table constructor can be found in 3.4.9 - Table Constructors. It could be summed up by the example provided there:
a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
I really, really recommend using the Lua Reference Manual, it is an amazing helper.
I also highly encourage you to read some basic tutorials e.g. Learn Lua in 15 minutes. They should give you an overview of the language you are trying to use.

in `+': String can't be coerced into Fixnum (TypeError)

Currently working on a HackerRank problem in Ruby. When I try to compile
in `+': String can't be coerced into Fixnum (TypeError)
on the following line
print d + double
Which I don't understand since none of those two variables is a string.
i = 4
d = 4.0
s = 'HackerRank'
# Declare second integer, double, and String variables.
intOne = 12
double = 4.0
string = "is the best place to learn and practice coding!, we get HackerRank is the best place to learn and practice coding!"
# Read and save an integer, double, and String to your variables.
intOne = gets.chomp
double = gets.chomp
string = gets.chomp
# Print the sum of both integer variables on a new line.
print i + intOne
# Print the sum of the double variables on a new line.
print d + double
# Concatenate and print the String variables on a new line
print s + string
# The 's' variable above should be printed first.
You must call method .to_s on your integer/ float if you want add it to something string
for example:
i = 3
b = ' bah '
c = i.to_s + b
# => '3 bah'
or if you have string like this: '3', and you want get from this string integer you must call to_i method if you want iteger, to_f it you want float
for example:
i = '3'
g = i.to_f
# => 3
You have defined double two times:
double = 4.0 #Float type
double = gets.chomp #String type
So, double of String type has overridden Float type.
You have defined:
d = 4.0 #Float type
So when you do:
print d + double #actually you are doing here (Float + String)
double is a String due to gets.chomp

Printing the same character several times without a loop

I would like to "beautify" the output of one of my Dart scripts, like so:
-----------------------------------------
OpenPGP signing notes from key `CD42FF00`
-----------------------------------------
<Paragraph>
And I wonder if there is a particularly simple and/or optimized way of printing the same character x times in Dart. In Python, print "-" * x would print the "-" character x times.
Learning from this answer, for the purpose of this question, I wrote the following minimal code, which makes use of the core Iterable class:
main() {
// Obtained with '-'.codeUnitAt(0)
const int FILLER_CHAR = 45;
String headerTxt;
Iterable headerBox;
headerTxt = 'OpenPGP signing notes from key `CD42FF00`';
headerBox = new Iterable.generate(headerTxt.length, (e) => FILLER_CHAR);
print(new String.fromCharCodes(headerBox));
print(headerTxt);
print(new String.fromCharCodes(headerBox));
// ...
}
This gives the expected output, but is there a better way in Dart to print a character (or string) x times? In my example, I want to print the "-" character headerTxt.length times.
The original answer is from 2014, so there must have been some updates to the Dart language: a simple string multiplied by an int works.
main() {
String title = 'Dart: Strings can be "multiplied"';
String line = '-' * title.length
print(line);
print(title);
print(line);
}
And this will be printed as:
---------------------------------
Dart: Strings can be "multiplied"
---------------------------------
See Dart String's multiply * operator docs:
Creates a new string by concatenating this string with itself a number of times.
The result of str * n is equivalent to str + str + ...(n times)... + str.
Returns an empty string if times is zero or negative.
I use this way.
void main() {
print(new List.filled(40, "-").join());
}
So, your case.
main() {
const String FILLER = "-";
String headerTxt;
String headerBox;
headerTxt = 'OpenPGP signing notes from key `CD42FF00`';
headerBox = new List.filled(headerTxt.length, FILLER).join();
print(headerBox);
print(headerTxt);
print(headerBox);
// ...
}
Output:
-----------------------------------------
OpenPGP signing notes from key `CD42FF00`
-----------------------------------------

How do I parse a string at compile time in Nimrod?

Going through the second part of Nimrod's tutorial I've reached the part were macros are explained. The documentation says they run at compile time, so I thought I could do some parsing of strings to create myself a domain specific language. However, there are no examples of how to do this, the debug macro example doesn't display how one deals with a string parameter.
I want to convert code like:
instantiate("""
height,f,132.4
weight,f,75.0
age,i,25
""")
…into something which by hand I would write like:
var height: float = 132.4
var weight: float = 75.0
var age: int = 25
Obviously this example is not very useful, but I want to look at something simple (multiline/comma splitting, then transformation) which could help me implement something more complex.
My issue here is how does the macro obtain the input string, parse it (at compile time!), and what kind of code can run at compile time (is it just a subset of a languaje? can I use macros/code from other imported modules)?
EDIT: Based on the answer here's a possible code solution to the question:
import macros, strutils
# Helper proc, macro inline lambdas don't seem to compile.
proc cleaner(x: var string) = x = x.strip()
macro declare(s: string): stmt =
# First split all the input into separate lines.
var
rawLines = split(s.strVal, {char(0x0A), char(0x0D)})
buf = ""
for rawLine in rawLines:
# Split the input line into three columns, stripped, and parse.
var chunks = split(rawLine, ',')
map(chunks, cleaner)
if chunks.len != 3:
error("Declare macro syntax is 3 comma separated values:\n" &
"Got: '" & rawLine & "'")
# Add the statement, preppending a block if the buffer is empty.
if buf.len < 1: buf = "var\n"
buf &= " " & chunks[0] & ": "
# Parse the input type, which is an abbreviation.
case chunks[1]
of "i": buf &= "int = "
of "f": buf &= "float = "
else: error("Unexpected type '" & chunks[1] & "'")
buf &= chunks[2] & "\n"
# Finally, check if we did add any variable!
if buf.len > 0:
result = parseStmt(buf)
else:
error("Didn't find any input values!")
declare("""
x, i, 314
y, f, 3.14
""")
echo x
echo y
Macros can, by and large, utilize all pure Nimrod code that a procedure in the same place could see, too. E.g., you can import strutils or peg to parse your string, then construct output from that. Example:
import macros, strutils
macro declare(s: string): stmt =
var parts = split(s.strVal, {' ', ','})
if len(parts) != 3:
error("declare macro requires three parts")
result = parseStmt("var $1: $2 = $3" % parts)
declare("x, int, 314")
echo x
"Calling" a macro will basically evaluate it at compile time as though it were a procedure (with the caveat that the macro arguments will actually be ASTs, hence the need to use s.strVal above instead of s), then insert the AST that it returns at the position of the macro call.
The macro code is evaluated by the compiler's internal virtual machine.

Resources