Competitive programming in Nim
2021-9-26 07:0:0
Author: maskray.me(查看原文)
阅读量:18
收藏
In the afternoon, I came cross the Nim programming language again on Lobsters. I first learned some basics of the language in 2015, but had not touched it since then.
"Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.", according to its website.
An idea popped into my mind: why not solve some coding challenges in Nim?
As a niche language, it is not supported on many coding challenge websites. Fortunately, the Nim compiler generates C code. With a small amount of work, we can build a self-contained C source file suitable for submission.
Let's take a LeetCode challenge as an example. We write the main algorithm in Nim and use the emit pragma to write a C wrapper.
Prepare nim.cfg and run nim c -d:danger --gc:arc -d:useMalloc a.nim to generate a_comb.c:
-d:useMalloc avoids Nim's own memory manager and can greatly decrease the C code size. There are several choices for --gc, but --gc:arc can genreated the smallest C code. Unfortunately --gc:none does not work.
The LeetCode environment includes some glibc headers like <stdio> which result in some conflicts due to duplicate definitions of struct _IO_FILE and some GNU extern inline functions. Let's write a Nim program to remove the duplicate definitions.
import strutils var body: seq[string] = @[] i = 0 for line in lines "a_comb.c": body.add(line)
while i < body.len: if body[i].startsWith("struct _IO_FILE {"): whilenot body[i].startsWith "}": i += 1 i += 1 continue if body[i].startsWith("__inline extern") or body[i].startsWith("int main("): var c = 0 var changed = false i += 1 whiletrue: c += body[i].count('{') - body[i].count('}') changed = changed or c > 0 i += 1 if c == 0and changed: break continue
echo body[i] i += 1
1
nim c --run --verbosity:0 x > for-submit.c
Finally, run { echo '/*'; cat a.nim; echo '*/'; cat for-submit.c; } | xclip -i -selection clipboard and paste the content into the LeetCode editor:) Well, the Nim source is not really needed but it is useful for archive purposes.
for-submitted.c is suitable for 64-bit Linux environment.
TODO Figure out how to support Codeforces (Windows with a 64KiB source code size limit).
If CIL fails to handle some syntax, use another deduplicator:
from collections import Counter import re import sys l = list(i.rstrip() for i in sys.stdin.readlines()) seen = Counter() include = set() i = 0 output = [] intbits = -1
while i < len(l): if l[i] == '#define NIM_INTBITS 64'and intbits == -1: intbits = i
if l[i] == '#include "nimbase.h"': i += 1 continue
m = re.match(r'#include <(.*)>', l[i]) if m: include.add(m.group(1)) i += 1 continue
if l[i].startswith('N_LIB_PRIVATE '): l[i] = l[i][14:]
m = re.match(r'struct (\w+) \{$', l[i]) if m: seen[m.group(1)] += 1 if m and seen[m.group(1)] > 1: c = 0 changed = False whileTrue: c += l[i].count('{') - l[i].count('}') changed = changed or c > 0 i += 1 if c == 0and changed: break continue
m = re.match(r'static N_INLINE\(.*, (\w+)\)\(.*\) \{$', l[i]) if m: seen[m.group(1)] += 1 if m and seen[m.group(1)] > 1or l[i].startswith('int main('): c = 0 changed = False whileTrue: c += l[i].count('{') - l[i].count('}') changed = changed or c > 0 i += 1 if c == 0and changed: break continue
output.append(l[i]) i += 1
with open('/home/maskray/Util/Nim/nimbase.min.h') as f: nimbase = list(i.rstrip() for i in f.readlines())
output = output[:intbits] + nimbase + output[intbits:] for i in include: print(f'#include <{i}>') for line in output: print(line)