Last active
August 29, 2015 14:13
-
-
Save s-zeid/c4ffd325a6603f0697db to your computer and use it in GitHub Desktop.
.I <3 Nim. — Scott Zeid's Nim playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.nimcache | |
*.nimc | |
*.swp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# From: <https://nim-by-example.github.io/oop_macro/> | |
# Public domain via: | |
# <https://github.com/nim-by-example/nim-by-example.github.io/blob/master/LICENCE> | |
# As of: 2015-01-11 | |
import macros | |
macro class*(head: expr, body: stmt): stmt {.immediate.} = | |
# The macro is immediate so that it doesn't | |
# resolve identifiers passed to it | |
var typeName, baseName: PNimrodNode | |
if head.kind == nnkIdent: | |
# `head` is expression `typeName` | |
# echo head.treeRepr | |
# -------------------- | |
# Ident !"Animal" | |
typeName = head | |
elif head.kind == nnkInfix and $head[0] == "of": | |
# `head` is expression `typeName of baseClass` | |
# echo head.treeRepr | |
# -------------------- | |
# Infix | |
# Ident !"of" | |
# Ident !"Animal" | |
# Ident !"TObject" | |
typeName = head[1] | |
baseName = head[2] | |
else: | |
quit "Invalid node: " & head.lispRepr | |
# echo treeRepr(body) | |
# -------------------- | |
# StmtList | |
# VarSection | |
# IdentDefs | |
# Ident !"name" | |
# Ident !"string" | |
# Empty | |
# IdentDefs | |
# Ident !"age" | |
# Ident !"int" | |
# Empty | |
# MethodDef | |
# Ident !"vocalize" | |
# Empty | |
# Empty | |
# FormalParams | |
# Ident !"string" | |
# Empty | |
# Empty | |
# StmtList | |
# StrLit ... | |
# MethodDef | |
# Ident !"age_human_yrs" | |
# Empty | |
# Empty | |
# FormalParams | |
# Ident !"int" | |
# Empty | |
# Empty | |
# StmtList | |
# DotExpr | |
# Ident !"this" | |
# Ident !"age" | |
# create a new stmtList for the result | |
result = newStmtList() | |
# var declarations will be turned into object fields | |
var recList = newNimNode(nnkRecList) | |
# Iterate over the statements, adding `this: T` | |
# to the parameters of functions | |
for node in body.children: | |
case node.kind: | |
of nnkMethodDef, nnkProcDef: | |
# inject `this: T` into the arguments | |
let p = copyNimTree(node.params) | |
p.insert(1, newIdentDefs(ident"this", typeName)) | |
node.params = p | |
result.add(node) | |
of nnkVarSection: | |
# variables get turned into fields of the type. | |
for n in node.children: | |
recList.add(n) | |
else: | |
result.add(node) | |
# The following prints out the AST structure: | |
# | |
# import macros | |
# dumptree: | |
# type X = ref object of Y | |
# z: int | |
# -------------------- | |
# TypeSection | |
# TypeDef | |
# Ident !"X" | |
# Empty | |
# RefTy | |
# ObjectTy | |
# Empty | |
# OfInherit | |
# Ident !"Y" | |
# RecList | |
# IdentDefs | |
# Ident !"z" | |
# Ident !"int" | |
# Empty | |
result.insert(0, | |
if baseName == nil: | |
quote do: | |
type `typeName` = ref object | |
else: | |
quote do: | |
type `typeName` = ref object of `baseName` | |
) | |
# Inspect the tree structure: | |
# | |
# echo result.treeRepr | |
# -------------------- | |
# StmtList | |
# StmtList | |
# TypeSection | |
# TypeDef | |
# Ident !"Animal" | |
# Empty | |
# RefTy | |
# ObjectTy | |
# Empty | |
# OfInherit | |
# Ident !"TObject" | |
# Empty <= We want to replace this | |
# MethodDef | |
# ... | |
result[0][0][0][2][0][2] = recList | |
# Lets inspect the human-readable version of the output | |
# echo repr(result) | |
# Output: | |
# type | |
# Animal = ref object of TObject | |
# name: string | |
# age: int | |
# | |
# method vocalize(this: Animal): string = | |
# "..." | |
# | |
# method age_human_yrs(this: Animal): int = | |
# this.age |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NIMFLAGS = | |
all: test.nimc | |
%.nimc: %.nim | |
nim c --nimcache:.nimcache $(NIMFLAGS) -o:"$@" "$^" | |
.PHONY: clean | |
clean: | |
rm -f test.nimc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# vim:se fdm=expr fde=getline(v\:lnum)=~'^[a-z]\\+\\(\ [^\:=]\\+\\)\\?[\:=]\\?$'?'>1'\:'=': | |
# (The modeline folds top-level blocks using a regular expression. | |
# Vim will execute the value of `fde` (short for `foldexpr`) in a sandbox: | |
# <http://vimhelp.appspot.com/options.txt.html#'foldexpr'>) | |
## Ceci n'est pas une comment. It is a documentation comment, since it starts | |
## with two #s, and it is part of the syntax tree, unlike a regular comment. | |
# Acts like `when false` | |
template note(label: expr=nil, body: stmt=nil) {.immediate.} = discard | |
# For organizational purposes. | |
# The label is stringified and stored in currentSection. | |
# Unlike `block`, no new scope is created, and our label can be any expression | |
# that can be stringified, not just an identifier. | |
var currentSection: string = nil | |
template section(body: auto) = section "": body | |
template section(label: expr, body: auto) = | |
currentSection = $(label) | |
body | |
currentSection = nil | |
section "Allows us to cleanly keep track of our main procs": | |
proc main() | |
type | |
MainProc = tuple[name: string, `proc`: proc() {.locks: 0.}, section: string] | |
MainProcs = seq[MainProc] | |
var mainProcs: MainProcs = @[] | |
template `&=`(main: proc(), `proc`: expr) = | |
block: | |
var mainProc: MainProc = (astToStr(`proc`), `proc`, currentSection) | |
mainProcs.add(mainProc) | |
note "Usage": | |
proc someProc() = | |
echo "Hello world" | |
main &= someProc | |
# then at runtime... | |
# %% someProc %% | |
# Hello world | |
# (see `proc main` at the end of the file) | |
# (Overloading `=` is on the roadmap for Nim 1.0.) | |
section "stuffs": | |
# not quite a comment; indentation and syntax still matter | |
note "ELF symbol table specimens": | |
proc debug(args: varargs[string]) = debug_104004 | |
proc bnay() = bnay_104015 | |
class HelloWorld of RootObj: # not in symbol table | |
method name: string = name_104020 name_104028 | |
method `name=`(name) = nameHEX3D_104046 nameHEX3D_104064 | |
method readName() = readname_104040 readname_104070 | |
method greet() = greet_105058 greet_105069 | |
proc fuckOff() {.exportc.} = fuckOff | |
class HelloPerson of HelloWorld: # likewise not in symbol table | |
method greet() = greet_105124 | |
proc fuckOff() = fuckoff_104174 | |
proc hello() {.exportc.} = hello | |
import classes; note: | |
# The classes.nim file in this repo contains a macro that allows us to do: | |
class ClassName of Superclass: | |
var x: string | |
method methodName: string = this.x | |
# instead of: | |
type ClassName = ref object of SuperClass | |
x: string | |
method methodName(this: ClassName) = this.x | |
# ... as in the first part literally becomes the second part | |
proc debug(args: varargs[string]) = | |
stderr.write("%") | |
for s in args.items: | |
stderr.write(" " & s) | |
stderr.write("\n") | |
proc bnay() = | |
discard 1992 | |
section "Hello world": | |
class HelloWorld of RootObj: | |
# Properties! | |
var F_name: string; note: | |
# Convention would be FName, but I don't like capitalizing field names. | |
# However, only the *first* character of a Nim identifier is case-sensitive, | |
# and underscores are *ignored* in Nim identifiers, so `F_name` is actually | |
# the same as `FName`. | |
## | |
method name: string = | |
debug("getting name (in HelloWorld)") | |
result = if this.F_name != nil: this.F_name else: "world" | |
method `name=`(name) = | |
debug("setting name to", repr(name)) | |
this.FName = name | |
# Methods! (NB: `method` is dynamic binding; `proc` is static binding) | |
method readName() = | |
discard | |
method greet() = | |
echo("Hello ", this.name, "!") | |
proc fuckOff() {.exportc.} = | |
this.readName() | |
this.greet() | |
class HelloPerson of HelloWorld: # Inheritance! | |
method name: string = | |
debug("getting name (in HelloPerson)") | |
if this.F_name != nil: this.F_name else: "person" | |
method readName() = | |
var name: string | |
while name in [nil, ""]: | |
stdout.write("What's your name? ") | |
name = readLine(stdin) | |
this.name = name | |
method greet() = | |
echo("Hi, ", this.name, "!") | |
proc fuckOff() = | |
HelloWorld(this).fuckOff() # super calls! | |
echo("Now fuck off, you stupid nimrod!") | |
var hp: HelloPerson = nil | |
proc hello() {.exportc.} = | |
bnay() | |
var hw = HelloWorld() | |
hw.fuckOff() | |
hp = HelloPerson() | |
hp.fuckOff() | |
main &= hello | |
section "JSON Spam sketch": | |
import json | |
proc `.`(node: json.JsonNode, key: string): json.JsonNode = node[key] | |
proc `.=`(node: json.JsonNode, key: string, value: json.JsonNode) = node[key] = value | |
template times(n: expr, body: stmt): stmt = | |
for i in 1..n: | |
body | |
proc spamSketch() = | |
var sketch = json.parseJson("""{"spam": "Spam, spam, spam, spam,"}""") | |
2.times: | |
echo sketch.spam.str | |
sketch.spam = json.newJString"LOVELY SPAM, WONDERFUL SPAM" | |
3.times: | |
echo sketch.spam.str | |
sketch.spam = json.newJString"SHUT UP!" | |
3.times: | |
echo sketch.spam.str | |
main &= spamSketch | |
section "HTTP Spam menu": | |
import strutils | |
type Header = tuple[name: string, value: string] | |
proc `&=`(req: var string, header: Header) = | |
req &= header.name & ": " & header.value & "\r\l" | |
proc httpRequest(`method` = "GET", host, path: string, headers: varargs[Header]): string = | |
var path = (if path.startsWith("/"): path else: "/" & path) | |
var req: string = "" | |
req &= `method`.toUpper() & " " & path & " HTTP/1.0\r\l" | |
req &= ("Host", host) | |
req &= ("User-Agent", "test.nim/1.0") | |
for header in headers.items: | |
req &= header | |
req &= "\r\l" | |
return req | |
import net | |
from rawsockets import AF_INET6 | |
proc getSpamMenu() = | |
var name = (if hp != nil: hp.name else: "nil") | |
var req = httpRequest("HEAD", host="www.greenmidgetcafe.com", path="menu", | |
("X-User-Name", name)) | |
echo req | |
var sock = net.newSocket(AF_INET6, buffered=false) | |
sock.connect("www.greenmidgetcafe.com", net.Port(80), AF_INET6) | |
sock.send(req) | |
var data: string = ""; | |
while sock.recv(data, 1) != 0: | |
stdout.write(data) | |
sock.close() | |
main &= getSpamMenu | |
section: | |
proc inUnnamedSection() = | |
echo "This proc is defined in an unnamed section." | |
main &= inUnnamedSection | |
proc notInSection() = | |
echo "This proc is not in a section." | |
main &= notInSection | |
proc main() = | |
for i in 1..mainProcs.len: | |
var mainProc = mainProcs[i-1] | |
var procSyntax = "proc " & mainProc.name & "()" | |
var sectionDesc = | |
if mainProc.section != "": mainProc.section | |
else: "(unnamed section)" | |
stderr.write("%") | |
if unlikely(mainProc.section == nil): | |
debug(procSyntax, "%%") | |
else: | |
debug(sectionDesc, "-", procSyntax, "%%") | |
mainProc.`proc`() | |
if i != mainProcs.len: | |
echo "" | |
when isMainModule: | |
setControlCHook(proc() {.noconv.} = quit(1)) | |
main() | |
note "Python equivalent": | |
# if __name__ == "__main__": | |
# try: | |
# main() | |
# except KeyboardInterrupt: | |
# pass | |
## |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% Hello world - proc hello() %% | |
% getting name (in HelloWorld) | |
Hello world! | |
What's your name? Scott | |
% setting name to 0x7fc96f206050"Scott" | |
% getting name (in HelloPerson) | |
Hi, Scott! | |
Now fuck off, you stupid nimrod! | |
%% JSON Spam sketch - proc spamSketch() %% | |
Spam, spam, spam, spam, | |
Spam, spam, spam, spam, | |
LOVELY SPAM, WONDERFUL SPAM | |
LOVELY SPAM, WONDERFUL SPAM | |
LOVELY SPAM, WONDERFUL SPAM | |
SHUT UP! | |
SHUT UP! | |
SHUT UP! | |
%% HTTP Spam menu - proc getSpamMenu() %% | |
% getting name (in HelloPerson) | |
HEAD /menu HTTP/1.0 | |
Host: www.greenmidgetcafe.com | |
User-Agent: test.nim/1.0 | |
X-User-Name: Scott | |
HTTP/1.0 404 Not Found | |
Content-Type: text/html | |
Content-Length: 345 | |
Connection: close | |
Date: Mon, 12 Jan 2015 09:48:58 GMT | |
Server: lighttpd/bnay | |
%% (unnamed section) - proc inUnnamedSection() %% | |
This proc is defined in an unnamed section. | |
%% proc notInSection() %% | |
This proc is not in a section. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is free and unencumbered software released into the public domain. | |
Anyone is free to copy, modify, publish, use, compile, sell, or | |
distribute this software, either in source code form or as a compiled | |
binary, for any purpose, commercial or non-commercial, and by any | |
means. | |
In jurisdictions that recognize copyright laws, the author or authors | |
of this software dedicate any and all copyright interest in the | |
software to the public domain. We make this dedication for the benefit | |
of the public at large and to the detriment of our heirs and | |
successors. We intend this dedication to be an overt act of | |
relinquishment in perpetuity of all present and future rights to this | |
software under copyright law. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
OTHER DEALINGS IN THE SOFTWARE. | |
For more information, please refer to <http://unlicense.org> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment