How to catch the output of a compiler error in nim? - nim-lang

I am not sure if this is currently possible (and maybe it is not even advisable), but I would like to be able to catch the output of a compiler error and reuse it in code. An example would be:
type IntOrString = int | string
var s: seq[IntOrString]
this code would not compile with error:
/usercode/in.nim(2, 5) Error: invalid type: 'IntOrString' in this context: 'seq[IntOrString]' for var
I am interested in a way to be able to use the message of this error in the code.
My use case is being able to easily document and discuss compiler errors in nimib. If I were to write a document that shows and discuss the different type of compiler errors catching the message automatically that could be useful (a workaround right now would be to write the code to file and compile with verbosity 0).

It is possible to use the compiler api to catch errors:
import ../compiler/[nimeval, llstream, ast, lineinfos, options], os, strformat
let std = findNimStdLibCompileTime()
let modules = [std, std / "pure", std / "std", std / "core"]
var intr = createInterpreter("script", modules)
intr.registerErrorHook proc(config:ConfigRef,info:TLineInfo,msg:string,severity:Severity) =
raise newException(CatchableError,&"{severity}: {(info.line,info.col)} {msg}")
)
try:
intr.evalScript(llStreamOpen("""type IntOrString = int | string
var s: seq[IntOrString]"""))
except CatchableError as e:
echo e.msg
echo "done"
destroyInterpreter(intr)
outputs:
Error: (2, 4) invalid type: 'IntOrString' in this context: 'seq[IntOrS
tring]' for var
done
Caveat: you can't run runtime code at compile time, for example, trying to run
type Dog = object of RootObj
method speak(i:Dog):string = "woof"
echo Dog().speak()
in the interpreter will give you errors, instead you would have to do something like:
type Dog = object of RootObj
method speak*(i:Dog):string = "woof"
proc newDog*():Dog = discard
let
dog = intr.callRoutine(intr.selectRoutine("newDog"),[])
speech = intr.callRoutine(intr.selectRoutine("speak"),[dog])
if speech.kind == nkStrLit:
echo speech.strval

Related

How to mark non-pure function as pure in Nim

Is there a way to mark non-pure function p as pure? Maybe with some pragma?
I'm using p for debug, and it can't be used inside pure func procs.
playground
proc p(message: string): void = echo message
func purefn: void =
p "track"
purefn()
Error:
/usercode/in.nim(3, 6) Error: 'purefn' can have side effects
Well, for a start you can just use debugEcho instead of echo - it has no side effects (and it's specifically made for use-cases like that).
In other cases you can "lie" to the compiler by doing:
proc p(message: string) =
{.cast(noSideEffect).}:
echo message
func purefn =
p "track"
purefn()
as described in https://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma, but I would advise against it.
For your case, you can use debugEcho inside of echo which fakes having no side effects.
Other than that, you can use the {.cast(noSideEffect).} pragma if you're not using echo in your real code:
proc p(message: string): void = echo message
func purefn: void =
{.cast(noSideEffect).}:
p "track"
purefn()

Does Nim language has shorter notation for object initialization?

The example from docs
type
BinaryTree*[T] = ref object # BinaryTree is a generic type with
# generic param ``T``
le, ri: BinaryTree[T] # left and right subtrees; may be nil
data: T # the data stored in a node
proc newNode*[T](data: T): BinaryTree[T] =
# constructor for a node
new(result)
result.data = data
Is it possible to use something like one-line shortcut like
proc newNode*[T](data: T): BinaryTree[T] =
data.new(data = data)
The tutorial says: "Note that referential data types will be nil at the start of the procedure, and thus may require manual initialisation" and here "To allocate a new traced object, the built-in procedure new must be used". But if you really need to save that line, you can make a template:
template aNewNode(data: untyped): void =
new(result)
result.data = data
proc newNode*[T](data: T): BinaryTree[T] =
# constructor for a node
aNewNode data

How to solve gcc [-Werror=format-security] in function call?

I have this call to the czmq api:
int rc = zsock_connect(updates, ("inproc://" + uuidStr).c_str());
(Note: uuidStr is of type std::string and zsock_connect expects a const char* as its second argument)
Which gives compiler error:
error: format not a string literal and no format arguments [-Werror=format-security]
int rc = zsock_connect(updates, ("inproc://" + uuidStr).c_str());
^
I've tried:
const char* connectTo = ("inproc://" + uuidStr).c_str();
int rc = zsock_connect(updates, connectTo);
and also
int rc = zsock_connect(updates, (const char*)("inproc://" +
uuidStr).c_str());
But the error persists.
How do I correct this?
Context; I'm trying to compile this code as a Python extension on Linux using pip install. On Windows it compiles with pip install and runs just fine, presumably that compiler is more permissive.
This function acts like printf() and friends, right? If so, you have the same problem with that that exists with printf(some_var) - if the string you're passing has format sequences in it, you get undefined behavior and bad stuff happening because there aren't the arguments present that you're telling the function to expect. The fix is to do something like:
int rc = zsock_connnect(updates, "inproc://%s", uuidStr.c_str());
Basically, give it a format that takes your string as an argument.

Difference between String and StaticString

I was browsing the docs, and I found StaticString. It states:
An simple string designed to represent text that is "knowable at compile-time".
I originally thought that String has the same behaviour as NSString, which is known at compile time, but it looks like that I was wrong. So my question is when should we use StaticString instead of a String, and is the only difference is that StaticString is known at compile-time?
One thing I found is
var a: String = "asdf" //"asdf"
var b: StaticString = "adsf" //{(Opaque Value), (Opaque Value), (Opaque Value)}
sizeofValue(a) //24
sizeofValue(b) //17
So it looks like StaticString has a little bit less memory footprint.
It appears that StaticString can hold string literals. You can't assign a variable of type String to it, and it can't be mutated (with +=, for example).
"Knowable at compile time" doesn't mean that the value held by the variable will be determined at compile time, just that any value assigned to it is known at compile time.
Consider this example which does work:
var str: StaticString
for _ in 1...10 {
switch arc4random_uniform(3) {
case 0: str = "zero"
case 1: str = "one"
case 2: str = "two"
default: str = "default"
}
print(str)
}
Any time you can give Swift more information about how a variable is to be used, it can optimize the code using it. By restricting a variable to StaticString, Swift knows the variable won't be mutated so it might be able to store it more efficiently, or access the individual characters more efficiently.
In fact, StaticString could be implemented with just an address pointer and a length. The address it points to is just the place in the static code where the string is defined. A StaticString doesn't need to be reference counted since it doesn't (need to) exist in the heap. It is neither allocated nor deallocated, so no reference count is needed.
"Knowable at compile time" is pretty strict. Even this doesn't work:
let str: StaticString = "hello " + "world"
which fails with error:
error: 'String' is not convertible to 'StaticString'
StaticString is knowable at compile time. This can lead to optimizations. Example:
EDIT: This part doesn't work, see edit below
Suppose you have a function that calculates an Int for some String values for some constants that you define at compile time.
let someString = "Test"
let otherString = "Hello there"
func numberForString(string: String) -> Int {
return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}
let some = numberForString(someString)
let other = numberForString(otherString)
Like this, the function would be executed with "Test" and "Hello there" when it really gets called in the program, when the app starts for example. Definitely at runtime. However if you change your function to take a StaticString
func numberForString(string: StaticString) -> Int {
return string.stringValue.unicodeScalars.reduce(0) { $0 * 1 << 8 + Int($1.value) }
}
the compiler knows that the passed in StaticString is knowable at compile time, so guess what it does? It runs the function right at compile time (How awesome is that!). I once read an article about that, the author inspected the generated assembly and he actually found the already computed numbers.
As you can see this can be useful in some cases like the one mentioned, to not decrease runtime performance for stuff that can be done at compile time.
EDIT: Dániel Nagy and me had a conversation. The above example of mine doesn't work because the function stringValue of StaticString can't be known at compile time (because it returns a String). Here is a better example:
func countStatic(string: StaticString) -> Int {
return string.byteSize // Breakpoint here
}
func count(string: String) -> Int {
return string.characters.count // Breakpoint here
}
let staticString : StaticString = "static string"
let string : String = "string"
print(countStatic(staticString))
print(count(string))
In a release build only the second breakpoint gets triggered whereas if you change the first function to
func countStatic(string: StaticString) -> Int {
return string.stringValue.characters.count // Breakpoint here
}
both breakpoints get triggered.
Apparently there are some methods which can be done at compile time while other can't. I wonder how the compiler figures this out actually.

Error: expression has no address

# lines 11-12:
proc last[T](ll: seq[T]): var T =
return ll[high(ll)]
# line 118:
if last(formula)["state"] == c_empty:
Errors:
main.nim(118, 12) Info: template/generic instantiation from here
main.nim(12, 12) Error: expression has no address
What the compiler wants?
[] does not return a var.
I don't think you need to annotate anything in this snippet with var though, since nothing is being mutated. Specify var at the call site if needed.
Try to submit compilable examples in the future if you can.

Resources