- What is Nimrod?
- Implementation aspects
- "Hello World"
- Meta programming features
- Optimizing "Hello World" via term rewriting macros
- Hoisting via term rewriting macros
- Summary of Nimrod's meta programming features
varinput:TaintedString
- systems programming language
vara=cast[int](gch.stackBottom)
varinput:TaintedString
- systems programming language
vara=cast[int](gch.stackBottom)
iteratorfrom1to2():int=yield1;yield2
varinput:TaintedString
- systems programming language
vara=cast[int](gch.stackBottom)
iteratorfrom1to2():int=yield1;yield2
- and strong meta programming capabilities.
template`!=`(x,y:expr):expr=not(x==y)
- Nimrod compiles to C; C++ and Objective-C are also supported
- it compiles to JavaScript
- it provides a realtime GC which supports max pause times of 1-2 miliseconds which means it is perfectly useful even for demanding games
- the Nimrod compiler and all of the standard library (including the GC) are written in Nimrod
- whole program dead code elimination: stdlib carefully crafted to make use of it; for instance parsers do not use (runtime) regular expression -> re engine not part of the executable
- the GC is also optimized away if you do not use it
- our infrastructure (IDE, build farm, build tools, package manager) is also completely written in Nimrod
echo"hello ","world",99
echo"hello ","world",99
is rewritten to:
echo([$"hello ",$"world",$99])
- echo is declared as: proc echo(a: varargs[string, `$`]); $ (Nimrod's toString operator) is applied to every argument
- local type converter: only in this context everything is converted to a string via $
- in contrast to C#/Java's solution this requires no dynamic binding
echo"hello ","world",99
is rewritten to:
echo([$"hello ",$"world",$99])
- echo is declared as: proc echo(a: varargs[string, `$`]); $ (Nimrod's toString operator) is applied to every argument
- local type converter: only in this context everything is converted to a string via $
- in contrast to C#/Java's solution this requires no dynamic binding
- it is extensible:
proc`$`(x:MyObject):string=x.svarobj=MyObject(s:"xyz")echoobj
Nimrod's focus is meta programming; macros are used
1. to avoid code duplication / boilerplate:
01templatehtmlTag(tag:expr){.immediate.}=02proctag():string="<"&astToStr(tag)&">"0304htmlTag(br)05htmlTag(html)0607echobr()
Produces:
<br>
2. for control flow abstraction:
01templateonce(body:stmt)=02varx{.global.}=false03ifnotx:04x=true05body0607procp()=08once:09echo"first call of p"10echo"some call of p"1112p()13once:14echo"new instantiation"15p()
2. for control flow abstraction:
01templateonce(body:stmt)=02varx{.global.}=false03ifnotx:04x=true05body0607procp()=08once:09echo"first call of p"10echo"some call of p"1112p()13once:14echo"new instantiation"15p()
Produces:
first call of p some call of p new instantiation some call of p
3. for lazy evaluation:
01templatelog(msg:string)=02ifdebug:03echomsg0405log("x: "&$x&", y: "&$y)
4. to implement DSLs:
01htmlmainPage:02head:03title"now look at this"04body:05ul:06li"Nimrod is quite capable"0708echomainPage()
Produces:
<html> <head><title>now look at this</title></head> <body> <ul> <li>Nimrod is quite capable</li> </ul> </body> </html>
Implementation:
01templatehtml(name:expr,matter:stmt){.immediate.}=02procname():string=03result="<html>"04matter05result.add("</html>")0607templatenestedTag(tag:expr){.immediate.}=08templatetag(matter:stmt){.immediate.}=09result.add("<"&astToStr(tag)&">")10matter11result.add("</"&astToStr(tag)&">")1213templatesimpleTag(tag:expr){.immediate.}=14templatetag(matter:expr){.immediate.}=15result.add("<$1>$2</$1>"%[astToStr(tag),matter])1617nestedTagbody18nestedTaghead19nestedTagul20simpleTagtitle21simpleTagli
After macro expansion:
templatehtml(name:expr,matter:stmt){.immediate.}=procname():string=result="<html>"matterresult.add("</html>")templatehead(matter:stmt){.immediate.}=result.add("<"&astToStr(head)&">")matterresult.add("</"&astToStr(head)&">")...templatetitle(matter:expr){.immediate.}=result.add("<$1>$2</$1>"%[astToStr(title),matter])templateli(matter:expr){.immediate.}=result.add("<$1>$2</$1>"%[astToStr(li),matter])
01htmlmainPage:02head:03title"now look at this"04body:05ul:06li"Nimrod is quite capable"0708echomainPage()
Is translated into:
procmainPage():string=result="<html>"result.add("<head>")result.add("<$1>$2</$1>"%["title","now look at this"])result.add("</head>")result.add("<body>")result.add("<ul>")result.add("<$1>$2</$1>"%["li","Nimrod is quite capable"])result.add("</ul>")result.add("</body>")result.add("</html>")
Compile time function evaluation optimizes 'mainPage()' into:
"<html><head><title>now look at this</title></head><body>..."
5. to provide user defined optimizations (via term rewriting macros).
01type02MyObject=object03a,b:int04s:string05letobj=MyObject(a:3,b:4,s:"abc")06echoobj
Produces (roughly):
(a:3b:4s:"abc")
How does it work?
- $ for object is in Nimrod's system module (like Haskell's prelude)
0102proc`$`[T:object](x:T):string=03result="("04forname,valueinfieldPairs(x):05result.add("$1: $2\n"%[name,$value])06result.add(")")
Notes:
- [T: object] -- generic type + constraint
- relies on builtin fieldPairs iterator
- Builtin fieldPairs iterator transforms the loop into the following:
result="("result.add("$1: $2\n"%["a",$obj.a])result.add("$1: $2\n"%["b",$obj.b])result.add("$1: $2\n"%["s",$obj.s])result.add(")")
- Builtin fieldPairs iterator transforms the loop into the following:
result="("result.add("$1: $2\n"%["a",$obj.a])result.add("$1: $2\n"%["b",$obj.b])result.add("$1: $2\n"%["s",$obj.s])result.add(")")
Desired result:
result=`&`("(a: ",$obj.a,"\nb: ",$obj.b,"\ns: ",$obj.s,"\n)")
We need partial evaluation for '%'.
%'s implementation (simplified):
01proc`%`(f:string,a:openArray[string]):string=02result=""03vari=004whilei<f.len:05iff[i]=='$':06casef[i+1]07of'1'..'9':08varj=009i+=110whilef[i]in{'0'..'9'}:11j=j*10+ord(f[i])-ord('0');i+=112result.add(a[j-1])13else:14invalidFormatString()15else:16result.add(f[i]);i+=1
01macrooptFormat{`%`(f,a)}(f:string{lit},a:openArray[string]):expr=02result=newCall("&")03vari=004whilei<f.len:05iff[i]=='$':06casef[i+1]07of'1'..'9':08varj=009i+=110whilef[i]in{'0'..'9'}:11j=j*10+ord(f[i])-ord('0');i+=112result.add(a[j-1])13else:14invalidFormatString()15else:16result.add(newLit(f[i]));i+=1
Implements this optimization:
"$1: $2\n"%["s",$obj.s]-->`&`("s",':',' ',$obj.s,'\n')
result="("result.add("$1: $2\n"%["a",$obj.a])result.add("$1: $2\n"%["b",$obj.b])result.add("$1: $2\n"%["s",$obj.s])result.add(")")
After partial evaluation:
result="("result.add(`&`("a",':',' ',$obj.a,'\n'))result.add(`&`("b",':',' ',$obj.b,'\n'))result.add(`&`("s",':',' ',$obj.s,'\n'))result.add(")")
result="("result.add(`&`("a",':',' ',$obj.a,'\n'))result.add(`&`("b",':',' ',$obj.b,'\n'))result.add(`&`("s",':',' ',$obj.s,'\n'))result.add(")")
After constant folding:
result="("result.add(`&`("a: ",$obj.a,'\n'))result.add(`&`("b: ",$obj.b,'\n'))result.add(`&`("s: ",$obj.s,'\n'))result.add(")")
After constant folding:
result="("result.add(`&`("a: ",$obj.a,'\n'))result.add(`&`("b: ",$obj.b,'\n'))result.add(`&`("s: ",$obj.s,'\n'))result.add(")")
Further optimization via term rewriting templates:
templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&ztemplateoptAdd2{x.add(y);x.add(z)}(x,y,z:string)=x.add(y&z)
result="("result.add(`&`("a: ",$obj.a,'\n'))result.add(`&`("b: ",$obj.b,'\n'))result.add(`&`("s: ",$obj.s,'\n'))result.add(")")templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&z
result="("&`&`("a: ",$obj.a,'\n')result.add(`&`("b: ",$obj.b,'\n'))result.add(`&`("s: ",$obj.s,'\n'))result.add(")")templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&z
result="("&`&`("a: ",$obj.a,'\n')result.add(`&`("b: ",$obj.b,'\n'))result.add(`&`("s: ",$obj.s,'\n'))result.add(")")templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&z
result="("&`&`("a: ",$obj.a,'\n')&`&`("b: ",$obj.b,'\n')result.add(`&`("s: ",$obj.s,'\n'))result.add(")")templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&z
result="("&`&`("a: ",$obj.a,'\n')&`&`("b: ",$obj.b,'\n')result.add(`&`("s: ",$obj.s,'\n'))result.add(")")templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&z
result="("&`&`("a: ",$obj.a,'\n')&`&`("b: ",$obj.b,'\n')&`&`("s: ",$obj.s,'\n')result.add(")")templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&z
result="("&`&`("a: ",$obj.a,'\n')&`&`("b: ",$obj.b,'\n')&`&`("s: ",$obj.s,'\n')result.add(")")templateoptAdd1{x=y;x.add(z)}(x,y,z:string)=x=y&z
After applying these rules the code is:
result="("&`&`("a: ",$obj.a,'\n'))&`&`("b: ",$obj.b,'\n'))&`&`("s: ",$obj.s,'\n'))&")"
After constant folding (that the compiler performs for us) it becomes:
result=`&`("(a: ",$obj.a,"\nb: ",$obj.b,"\ns: ",$obj.s,"\n)")
- Implementation of % and optFormat look suspiciously alike.
- Can we avoid the code duplication?
01templateformatImpl(handleChar:expr)=02vari=003whilei<f.len:04iff[i]=='$':05casef[i+1]06of'1'..'9':07varj=008i+=109whilef[i]in{'0'..'9'}:10j=j*10+ord(f[i])-ord('0');i+=111result.add(a[j-1])12else:13invalidFormatString()14else:15result.add(handleChar(f[i]));i+=11617proc`%`(f:string,a:openArray[string]):string=18templateidentity(x:expr):expr=x19result="";formatImpl(identity)2021macrooptFormat{`%`(f,a)}(f:string{lit},a:openArray[string]):expr=22result=newCall("&");formatImpl(newLit)
01procre(x:string):Regex=020304templateoptRe{re(x)}(x:string{lit}):Regex=05varg{.global.}=re(x)06g0708template`=~`(s:string,pattern:Regex):bool=09whennotdefinedInScope(matches):10varmatches{.inject.}:array[maxSubPatterns,string]11match(s,pattern,matches)1213forlineinlines("input.txt"):14ifline=~re"(\w+)=(\w+)":15echo"key-value pair; key: ",matches[0]," value: ",matches[1]
- Perl performs same optimization; however regexes are built into Perl, but not into Nimrod
- 'inject' breaks hygiene
You name it, Nimrod got it (except fexprs ;-):
- compile time function evaluation; including staticRead and staticExec
- declarative (template) and imperative (macro) AST based macros: both hygienic and dirty
- term rewriting macros; side-effect and alias analysis constraints
- source code filters
- programmable annotation system ("pragmas")
01macrocheck(ex:expr):stmt=02varinfo=ex.lineInfo03varexpString=ex.toStrLit04result=quotedo:05ifnot`ex`:06echo`info`,": Check failed: ",`expString`0708check1<2
Thank you for listening. We are always looking for contributors:
- http://nimrod-code.org
- http://forum.nimrod-code.org
- https://github.com/Araq/Nimrod
- irc.freenode.net/nimrod
