How can I use expression inside formatted string in Nim? - nim-lang

The if expression is not working if used inside of fmt string.
Why, and how to make it work?
import strformat
let v = if true: 1 else: 2 # <= Works
echo fmt"{v}"
echo fmt"{if true: 1 else: 2}" # <= Error

why?
because fmt uses : to separate value of expression from format specifier so (see docs and implementation) the line
echo fmt"{if true: 1 else: 2}"
is expanded by the macro into
var temp = newStringOfCap(educatedCapGuess)
temp.formatValue if true, " 1 else: 2"
temp
which clearly does not compile.
how?
update
currently (April 2021) in devel branch there is a enhancement that allows to use any expression inside a formatted string. For the specific case mentioned you need to surround the expression by parenthesis:
echo fmt"{(if true: 1 else: 2)}"
the new enhancements also allow to use curly brackets in expression (escaping them).
See:
RFC: https://github.com/nim-lang/RFCs/issues/366
a first and a second PR that added the implementation
recent forum discussion: https://forum.nim-lang.org/t/7052
This enhancement will be released for the general public in the next stable version (likely to be 1.6).
old content
I guess it could be seen as a limitation of fmt and I do not think there is currently a way to use an expression with : in fmt where it does not act as a format specificier.
One way to fix this would be to provide an additional formatSpecifierSeparator keyword argument in order to change the default : and be able to do something like:
echo "{if true: 1 else: 2}".fmt('|')
Another way would be to change the implementation of strformatImpl and make sure that the part before a : actually compiles before interpreting : as a formatSpecifier separator.
Both of these ways imply a PR in nim-lang code and would be available after next release or on devel, if accepted and merged.

this works:
import std/strformat
let x = 3.14
assert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847"
assert fmt"{(if true: 1 else: 2)}" == "1"
assert fmt"{if true\: 1 else\: 2}" == "1"
and avoids conflicts with format specifier. See https://github.com/nim-lang/Nim/pull/17700 for more details.

Related

Lark: parsing special characters

I'm starting with Lark and got stuck on an issue with parsing special characters.
I have expressions given by a grammar. For example, these are valid expressions: Car{_}, Apple3{3+}, Dog{a_7}, r2d2{A3*}, A{+}... More formally, they have form: name{feature} where
name: CNAME
feature: (DIGIT|LETTER|"+"|"-"|"*"|"_")+
The definition of constants can be found here.
The problem is that the special characters are not present in produced tree (see example below). I have seen this answer, but it did not help me. I tried to place ! before special characters, escaping them. I also enabled keep_all_tokens, but this is not desired because then characters { and } are also present in the tree. Any ideas how to solve this problem? Thank you.
from lark import Lark
grammar = r"""
start: object
object : name "{" feature "}" | name
feature: (DIGIT|LETTER|"+"|"-"|"*"|"_")+
name: CNAME
%import common.LETTER
%import common.DIGIT
%import common.CNAME
%import common.WS
%ignore WS
"""
parser = Lark(grammar, parser='lalr',
lexer='standard',
propagate_positions=False,
maybe_placeholders=False
)
def test():
test_str = '''
Apple_3{3+}
'''
j = parser.parse(test_str)
print(j.pretty())
if __name__ == '__main__':
test()
The output looks like this:
start
object
name Apple_3
feature 3
instead of
start
object
name Apple_3
feature
3
+
You said you tried placing ! before special characters. As I understand the question you linked, the ! has to be replaced before the rule:
!feature: (DIGIT|LETTER|"+"|"-"|"*"|"_")+
This produces your expected result for me:
start
object
name Apple_3
feature
3
+

What input is imaplib's Internaldate2tuple expecting?

The API reference for Python 3 says imaplib's Internaldate2tuple parses an IMAP4 INTERNALDATE string, yet I can't find the information anywhere what that exactly means. The reverse function (Time2Internaldate) seems to produce one of these INTERNALDATE strings yet it's apparently not good enough for the Internaldate2tuple string. Is this a bug, shouldn't imaplib's functions be compatible with each other?
Is there any other way to parse the internaldate besides regexes and some strftime whatever magic?
from time import localtime
internaldate = imaplib.Time2Internaldate(localtime())
print("Coded: " + internaldate)
coded = imaplib.Internaldate2tuple(internaldate.encode('ascii'))
if coded == None:
print("Wrong format")
else:
print("Success")
internaldate = internaldate.replace('"', '')
print("Coded: " + internaldate)
coded = imaplib.Internaldate2tuple(internaldate.encode('ascii'))
if coded == None:
print("Wrong format")
else:
print("Success")
Output:
Coded: "14-Dec-2019 15:15:47 +0200"
Wrong format
Coded: 14-Dec-2019 15:15:47 +0200
Wrong format
Refer here
A sample input is:
Internaldate2tuple(b'INTERNALDATE "01-Jan-2000 12:00:00 +0000"')

Why does error 'unexpected character after line continuation character' appear here?

Does someone know what is wrong with this section of my code as it seems to cause errors. I'm new to programming so I'm not completely sure what's wrong.
menu = "Be Lenny's Friend?\n"
1. Yes\n\
2. No\n\
answer = int(input(menu))
if answer == 1:
print(" ( ͡° ͜ʖ ͡°): Yayyyy! We are going to be friends!")
elif answer == 2:
reason = input(" ( ͡° ʖ̯ ͡°): Why do you not want to be my friend :(")
Error message:
'unexpected character after line continuation character'
Here you have set the variable as a tuple of a string and... well that’s where things get confusing.
The backslash is a like continuation symbol as in you can then break the line and continue on and it would count as the same line. However what the interpreter sees is n: which makes no sense. That is what it is complaining about.
If you wanted to add a new line to the string itself, you could add the \n at the end of the string.
However, also note that if you printed the string using print in the vanilla form without any other arguments than the Adrianne itself, it will append a new line automatically. So if you do add the \n, it may still not be what you want when you print it out. Parameter can of course be changed in the print function to take care of that.
I'm not sure what you are trying to archive with ,\n\: at the end of the line, but this line of code will remove the syntax error:
menu = "Be Lenny's Friend?"
If you want to archive a new line after the string you need to move "\n" into the string like this:
menu = "Be Lenny's Friend?\n"
Edit:
This should work for you:
menu = "Be Lenny's Friend?\n\t1. Yes\n\t2. No\n"
answer = int(input(menu))
if answer == 1: print(" ( ͡° ͜ʖ ͡°): Yayyyy! We are going to be friends!")
elif answer == 2: reason = input(" ( ͡° ʖ̯ ͡°): Why do you not want to be my friend :(\n")

Gitlab CI: Set dynamic variables

For a gitlab CI I'm defining some variables like this:
variables:
PROD: project_package
STAGE: project_package_stage
PACKAGE_PATH: /opt/project/build/package
BUILD_PATH: /opt/project/build/package/bundle
CONTAINER_IMAGE: registry.example.com/project/package:e2e
I would like to set those variables a bit more dynamically, as there are mainly only two parts: project and package. Everything else depends on those values, that means I have to change only two values to get all other variables.
So I would expect something like
variables:
PROJECT: project
PACKAGE: package
PROD: $PROJECT_$PACKAGE
STAGE: $PROD_stage
PACKAGE_PATH: /opt/$PROJECT/build/$PACKAGE
BUILD_PATH: /opt/$PROJECT/build/$PACKAGE/bundle
CONTAINER_IMAGE: registry.example.com/$PROJECT/$PACKAGE:e2e
But it looks like, that the way doing this is wrong...
I don't know where your expectation comes from, but it is trivial to check there is no special meaning for $, _, '/' nor : if not followed by a space in YAML. There might be in gitlab, but I doubt strongly that there is in the way you expect.
To formalize your expectation, you assume that any key (from the same mapping) preceded by a $ and terminated by the end of the scalar, by _ or by / is going to be "expanded" to that key's value. The _ has to be such terminator otherwise $PROJECT_$PACKAGE would not expand correctly.
Now consider adding a key-value pair:
BREAKING_TEST: $PACKAGE_PATH
is this supposed to expand to:
BREAKING_TEST: /opt/project/build/package/bundle
or follow the rule you implied that _ is a terminator and just expand to:
BREAKING_TEST: project_PATH
To prevent this kind of ambiguity programs like bash use quoting around variable names to be expanded ( "$PROJECT"_PATH vs. $PROJECT_PATH), but the more sane, and modern, solution is to use clamping begin and end characters (e.g. { and }, $% and %, ) with some special rule to use the clamping character as normal text.
So this is not going to work as you indicated as indeed you do something wrong.
It is not to hard to pre-process a YAML file, and it can be done with e.g. Python (but watch out that { has special meaning in YAML), possible with the help of jinja2: load the variables, and then expand the original text using the variables until replacements can no longer be made.
But it all starts with choosing the delimiters intelligently. Also keep in mind that although your "variables" seem to be ordered in the YAML text, there is no such guarantee when the are constructed as dict/hash/mapping in your program.
You could e.g. use << and >>:
variables:
PROJECT: project
PACKAGE: package
PROD: <<PROJECT>>_<<PACKAGE>>
STAGE: <<PROD>>_stage
PACKAGE_PATH: /opt/<<PROJECT>>/build/<<PACKAGE>>
BUILD_PATH: /opt/<<PROJECT>>/build/<<PACKAGE>>/bundle
CONTAINER_IMAGE: registry.example.com/<<PROJECT>>/<<PACKAGE>>:e2
which, with the following program (that doesn't deal with escaping << to keep its normal meaning) generates your original, expanded, YAML exactly.
import sys
from ruamel import yaml
def expand(s, d):
max_recursion = 100
while '<<' in s:
res = ''
max_recursion -= 1
if max_recursion < 0:
raise NotImplementedError('max recursion exceeded')
for idx, chunk in enumerate(s.split('<<')):
if idx == 0:
res += chunk # first chunk is before <<, just append
continue
try:
var, rest = chunk.split('>>', 1)
except ValueError:
raise NotImplementedError('delimiters have to balance "{}"'.format(chunk))
if var not in d:
res += '<<' + chunk
else:
res += d[var] + rest
s = res
return s
with open('template.yaml') as fp:
yaml_str = fp.read()
variables = yaml.safe_load(yaml_str)['variables']
data = yaml.round_trip_load(expand(yaml_str, variables))
yaml.round_trip_dump(data, sys.stdout, indent=2)

Generating a specified number of sub-snippets using Ultisnips in vim

I currently have a tex.snippets file to hold snippets which make writing homework in LaTeX easier. For example, I have a snippet '2problem' of the form:
snippet 2problem
\begin{homeworkProblem}
\begin{enumerate}
\item[] $1
$2
\item[] $3
$4
\end{enumerate}
\end{homeworkProblem}
endsnippet
This gives me an easy way of starting 2-part problems. Is there a way, however, of making a snippet that outputs n-part problems? Right now I have separate snippets for separate number of problems, which is quite tedious.
I know, this is not an ultisnip solution, just in case : mu-template supports loops and recursion in snippets. That how I generate a switch-case construct from an enum definition in C and C++. It's kind of cumbersome to define, but it works well.
Use the snippet definition as:
post_jump "dynamicsnippet(snip)"
snippet '(\d+)problem' rb
\begin{homeworkProblem}
\begin{enumerate}
`!p snip.rv=create_partprob(int(match.group(1)))`
\end{enumerate}
\end{homeworkProblem}
endsnippet
Attach this piece of code at the end of the desired snippets file.(Probably something like tex.snippets)
global !p
def dynamicsnippet(snip):
# Create anonymous snippet body
anon_snippet_body = ""
# Get start and end line number of expanded snippet
start = snip.snippet_start[0]
end = snip.snippet_end[0]
# Append current line into anonymous snippet
for i in range(start, end + 1):
anon_snippet_body += snip.buffer[i]
anon_snippet_body += "" if i == end else "\n"
# Delete expanded snippet line till second to last line
for i in range(start, end):
del snip.buffer[start]
# Empty last expanded snippet line while preserving the line
snip.buffer[start] = ''
# Expand anonymous snippet
snip.expand_anon(anon_snippet_body)
def create_partprob(n):
out=""
placeholder=1
for _ in range(0,n):
out+=24*" "+"\\item[] $"+f"{placeholder}\\\\\\\\\n"
placeholder+=1
out+=24*" "+" $"+f"{placeholder}\\\\\\\\\n"
placeholder+=1
out=out[:-1]
return out
endglobal
It worked in my editor and Ultisnips, hope it works in yours as well.
Goodluck!
Edit: Use the stack specifically made for vim and vi related questions for such questions. You can find the site here.

Resources