Varna Conf, Юли 2014
Автори Захари Караджов, Борислав Станимиров
a + b * c
Представете си любимия си език, любимата си библиотека, любимия си код.
Всички преимущества, които виждате в тях, могат успешно да се постигнат Nimrod. А също така и голяма част от недостатъците им да изчезнат.
Nimrod има потенциала да стане най-използваният език на света вселената.
Markaby.nim
type Article = object | |
title, body: string | |
proc myPage(articles: seq[Article]): string = | |
return html: | |
head: | |
title "Боза.цом" | |
css "moar-blink.css" | |
body: | |
ul: | |
for article in articles: | |
li: | |
h2 article.title | |
p article.body |
template html(matter: stmt) {.dirty.} = | |
var result = "" | |
matter | |
template tag(tagName) = | |
template `tagName`(attrs: varargs[expr], matter: stmt = nil) {.dirty.} = | |
# formatAttrs затваря тага и при нужда добавя inner текста | |
result.add("<" & astToStr(tagName) & formatAttrs(attrs)) | |
matter | |
result.add("</" & astToStr(tagName) & ">") | |
tag head; tag link; tag body | |
tag ul; tag li; tag title | |
tag p; tag h2 | |
template css(file) {.dirty.} = | |
link(relation = "stylesheet", href = file) | |
macro formatAttrs(args: seq[expr]): expr = | |
result = newCall("&") | |
var innerTexts = newSeq[string]() | |
for arg in args: | |
if arg.kind == nnkExprEqExpr: | |
result.addParams($arg[0], "=", quoteString($arg[1])) | |
else: | |
innerTexts.add($arg) | |
result.addParams ">" | |
result.addParams innerTexts |
proc myPage(articles: seq[Article]): string = | |
var result = "" | |
result.add("<" & "head" & ">") | |
result.add("<" & "title" & ">") | |
result.add("Боза.цом") | |
result.add("</" & "title" & ">") | |
result.add("<link " & "relation" & "=" & "\"stylesheet\"" & | |
"href" & "=" & "\"moar-blink.css\"" & ">") | |
result.add("</" & "link" & ">") | |
result.add("</" & "head" & ">") | |
result.add("<" & "body" & ">") | |
result.add("<" & "ul" & ">") | |
for article in articles: | |
result.add("<" & "li" & ">") | |
result.add("<" & "h2" & ">") | |
result.add article.title | |
result.add("</" & "h2" & ">") | |
result.add("<" & "p" & ">") | |
result.add article.body | |
result.add("</" & "p" & ">") | |
result.add("</" & "li" & ">") | |
result.add("</" & "ul" & ">") | |
result.add("</" & "body" & ">") | |
... ту дъ рескю
template optAdd1 {x = y; x.add(z)} (x, y, z: string) = | |
x = y & z | |
template optAdd2 {x.add(y); x.add(z)} (x, y, z: string) = | |
x.add(y & z) |
proc myPage(articles: seq[Article]): string = | |
var result = "" # Тези редове съответстват на правилото: | |
result.add("<" & "head" & ">") # {var x = y; x.add(z);} (x, y, z: string) = | |
# x = y & z | |
result.add("<" & "title" & ">") | |
result.add("Боза.цом") | |
result.add("</" & "title" & ">") | |
result.add("<link " & "relation" & "=" & "\"stylesheet\"" & | |
"href" & "=" & "\"moar-blink.css\"" & ">") | |
result.add("</" & "link" & ">") | |
result.add("</" & "head" & ">") | |
result.add("<" & "body" & ">") | |
result.add("<" & "ul" & ">") | |
for article in articles: | |
result.add("<" & "li" & ">") | |
result.add("<" & "h2" & ">") | |
result.add article.title | |
result.add("</" & "h2" & ">") | |
result.add("<" & "p" & ">") | |
result.add article.body | |
result.add("</" & "p" & ">") | |
result.add("</" & "li" & ">") | |
result.add("</" & "ul" & ">") | |
result.add("</" & "body" & ">") |
proc myPage(articles: seq[Article]): string = | |
var result = "" & "<" & "head" & ">") # След прилагане на правилото | |
result.add("<" & "title" & ">") # можем да го приложим отново за | |
# новополучения код | |
result.add("Боза.цом") | |
result.add("</" & "title" & ">") | |
result.add("<link " & "relation" & "=" & "\"stylesheet\"" & | |
"href" & "=" & "\"moar-blink.css\"" & ">") | |
result.add("</" & "link" & ">") | |
result.add("</" & "head" & ">") | |
result.add("<" & "body" & ">") | |
result.add("<" & "ul" & ">") | |
for article in articles: | |
result.add("<" & "li" & ">") | |
result.add("<" & "h2" & ">") | |
result.add article.title | |
result.add("</" & "h2" & ">") | |
result.add("<" & "p" & ">") | |
result.add article.body | |
result.add("</" & "p" & ">") | |
result.add("</" & "li" & ">") | |
result.add("</" & "ul" & ">") | |
result.add("</" & "body" & ">") |
proc myPage(articles: seq[Article]): string = | |
var result = ("" & "<" & "head" & ">" & "<" & "title" & ">") | |
result.add("Боза.цом") | |
result.add("</" & "title" & ">") | |
result.add("<link " & "relation" & "=" & "\"stylesheet\"" & | |
"href" & "=" & "\"moar-blink.css\"" & ">") | |
result.add("</" & "link" & ">") | |
result.add("</" & "head" & ">") | |
result.add("<" & "body" & ">") | |
result.add("<" & "ul" & ">") | |
for article in articles: | |
result.add("<" & "li" & ">") | |
result.add("<" & "h2" & ">") | |
result.add article.title | |
result.add("</" & "h2" & ">") | |
result.add("<" & "p" & ">") | |
result.add article.body | |
result.add("</" & "p" & ">") | |
result.add("</" & "li" & ">") | |
result.add("</" & "ul" & ">") | |
result.add("</" & "body" & ">") |
...и така нататък казах!
NimString myPage(const Sequence<Article>& articles) | |
{ | |
NimString result = | |
"<head><title>Боза.цом</title>" | |
"<link relation=\"\stylesheet\" href=\"moar-blink.css\></link></head>" | |
"<body><ul>"; | |
for(const auto& article: articles) | |
{ | |
result.add(Concat("<li><h2>", article.title, "</h2><p>", article.body, "</p></li>")); | |
} | |
result.add("</ul></body>"); | |
return result; | |
} |
RubyBind.nim
class Character | |
attr_reader :name, :health | |
def initialize(@name) | |
@health = 100 | |
end | |
def equip(@weapon) | |
puts "#{@name}: I will slash my enemies with my #{@weapon.name}" | |
end | |
def attack(name) | |
puts "#{@name}: Eat my #{@weapon.name}, #{name}" | |
enemy = find_character(name) | |
enemy.take_damage(@weapon.damage) | |
end | |
def take_damage(damage) | |
@health -= damage | |
puts "#{@name}: Aaaarghhhhh" | |
end | |
end |
import ruby | |
let characterClass = rubyGlobal("Character") | |
var c = characterClass.new("Bozo") | |
type Weapon = ref object | |
name: string | |
damage, durability: int | |
c.equip(Weapon(name: "Holy Sword of Ultimate Imbalance", | |
damage: 99999999, | |
durability: 666666666)) | |
c.attack("Kiro") |
Bozo: I will slash my enemies with my Holy Sword of Ultimate Imbalance Bozo: Eat my Holy Sword of Ultimate Imbalance, Kiro Kiro: Aaaarghhhhh
proc toRuby(v: int): RubyValue = rb_int2inum(v) | |
proc toRuby(v: string): RubyValue = rb_str_new2(v) | |
proc toRuby(v: ref object|tuple): RubyValue = | |
var rubyClass {.global.} = makeRubyType(v.type) | |
GC_ref(v) | |
return rb_data_object_alloc(rubyClass, nil, GC_unref, v) |
var NimrodModule = rb_define_module "nimrod" | |
proc makeRubyType(RegisteredType: type): RubyValue = | |
result = rb_define_class_under(NimrodModule, RegisteredType.name, nil) | |
for fieldName, fieldType in RegisteredType.fieldPairs: | |
proc getterImpl(self: RubyValue): RubyValue = | |
let | |
nativeSelf = cast[RegisteredType](rb_data_ptr(self))) | |
fieldValue = readField(fieldName, nativeSelf) | |
return fieldValue.toRuby | |
rb_define_method(result, fielName, getterImpl) | |
Експортирането на сетъри и методи е оставено за упражнение на читателя
proc `.=`*(self: RubyValue, field: static[string], value): RubyValue = | |
const rubyMethod = field & "=" | |
return rb_funcall(self, rubyMethod, 1, [value.toRuby]) | |
proc `.()`*(self: RubyValue, methodName: static[string], | |
rbArgs: varargs[RubyValue, toRuby]): RubyValue = | |
return rb_funcall(obj, methodName, rbArgs.len, rbArgs) |
a.zyx = b.xxy
jsonRequest("http://search.twitter.com/search.json?q=#VarnaConf").tweets[0].author
VALUE NimrodModule = rb_define_module("nimrod"); | |
VALUE getterImpl_Name(VALUE self) { | |
return static_cast<Weapon*>(rb_data_ptr(self))->name; } | |
// И същото за getterImpl_Damage и getterImpl_Durability | |
VALUE MakeRubyType_Weapon() { | |
VALUE weaponClass = rb_define_class_under(NimrodModule, "Weapon", NULL); | |
rb_define_method(weaponClass, "name", getterImpl_Name); | |
rb_define_method(weaponClass, "damage", getterImpl_Damage); | |
rb_define_method(weaponClass, "durability", getterImpl_Durability); | |
return weaponClass; | |
} | |
void toRuby(Weapon* weapon) { | |
static VALUE RubyWeapon = MakeRubyType_Weapon(); | |
GC_ref(weapon); | |
return rb_data_object_alloc(RubyWeapon, nil, GC_unref, weapon); | |
} | |
void main() { | |
VALUE character = rb_gv_get("Character"); | |
VALUE args0[] = {toRuby("Bozo")}; | |
rb_funcall(character, "new", 1, args0); | |
VALUE args1[] = {toRuby(new Weapon("Holy Sword of Ultimate Imbalance", 999, 666))}; | |
rb_funcall(character, "equip", 1, args1); | |
VALUE args2[] = {toRuby("Kiro")}; | |
rb_funcall(character, "attack", 1, args2); | |
} |
ping.nim
var options = parseOptions: | |
desc "ping - send ICMP ECHO_REQUEST packets to network hosts" | |
flags: | |
c count "Number of packets to send" default(-1) | |
i iface "Netword interface to use" optional | |
m ttl "Sets the TTL value for each packet" default(64) | |
t timeout "Specify a timeout, in seconds" default(3) | |
params host | |
echo options.host, options.timeout |
WorldCupTest.nim
suite "World Cup Simulator": | |
setup: | |
var brazil = Team.new("Brazil") | |
var germany = Team.new("Germany") | |
var match = Match.new(brazil, germany) | |
test "Exact Score": | |
match.elapseTime 30.minutes | |
check match.score == Score(0, 5) | |
test "Referee": | |
match.complete | |
check germany.defenders.yellowCards > 3 | |
# output is Check Failed [germany.defenders.yellowCards > 3] | |
# germany.defenders.yellowCards was 0 | |
# germany.defenders was ["Lahm", "Boateng", "Hummels"] | |
"През 2017-та Нимрод ке почне да става за сериозен софтуер"
"През 2019-та 'секи ке пише на Нимрод"
Nimrod / nimrod-lang.org
Захари Караджов / github.com/zah / @zaharyk
Борислав Станимиров / ibob.github.io / @natcbb
Презентацията е лицензирана с Creative Commons Признание 3.0