-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 482f8d0
Showing
8 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,2 @@ | ||
github: metagn | ||
custom: https://www.buymeacoffee.com/metagn |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,26 @@ | ||
name: dirtydeeds | ||
|
||
on: | ||
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
branches: [ "master" ] | ||
|
||
workflow_dispatch: | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: jiro4989/setup-nim-action@v1 | ||
|
||
- name: install nimbleutils | ||
run: nimble install -y https://github.com/metagn/nimbleutils@#HEAD | ||
|
||
- name: install dependencies | ||
run: nimble install -y | ||
|
||
- name: run tests | ||
run: nimble tests |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,2 @@ | ||
*.exe | ||
*.dll |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,14 @@ | ||
# dirtydeeds | ||
|
||
Quick and dirty partial application of calls with possible typed arguments. | ||
|
||
```nim | ||
import dirtydeeds, sequtils | ||
assert @[1, 2, 3].map(deed _ * 7) == @[7, 14, 21] | ||
assert @["A", "B", "C"].map(deed "foo" & (_: string)) == @["fooA", "fooB", "fooC"] | ||
assert @['a', 'f', 'A', '0', 'c'].filter(deed contains({'a'..'z'}, _)) == @['a', 'f', 'c'] | ||
``` | ||
|
||
More uses in tests. Note that this is currently only for partial application, | ||
things like `_ (_ - 1)` will not work. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,21 @@ | ||
# Package | ||
|
||
version = "0.1.0" | ||
author = "metagn" | ||
description = "macro for partially applied calls" | ||
license = "MIT" | ||
srcDir = "src" | ||
|
||
|
||
# Dependencies | ||
|
||
requires "nim >= 1.0.0" | ||
|
||
when (compiles do: import nimbleutils): | ||
import nimbleutils | ||
|
||
task tests, "run tests for multiple backends": | ||
when declared(runTests): | ||
runTests(backends = {c, js, nims}) | ||
else: | ||
echo "tests task not implemented, need nimbleutils" |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,112 @@ | ||
import macros | ||
|
||
proc extractParam(node, defaultType: NimNode, count: int): NimNode = | ||
result = nil | ||
case node.kind | ||
of nnkCallKinds, nnkObjConstr: | ||
# _(a) means param named a | ||
if node.len == 2 and node[0].kind == nnkIdent and node[0].eqIdent"_": | ||
result = newIdentDefs(node[1], defaultType) | ||
result.copyLineInfo(node) | ||
of nnkExprEqExpr, nnkAsgn: | ||
# default value | ||
result = extractParam(node[0], defaultType, count) | ||
if not result.isNil: | ||
result[2] = node[1] | ||
of nnkExprColonExpr: | ||
# type | ||
result = extractParam(node[0], defaultType, count) | ||
if not result.isNil: | ||
result[1] = node[1] | ||
of nnkIdent: | ||
if node.eqIdent"_": | ||
result = newIdentDefs(ident("_" & $count), defaultType) | ||
result.copyLineInfo(node) | ||
of nnkPar, nnkTupleConstr: | ||
if node.len == 1 and node[0].kind notin {nnkPar, nnkTupleConstr}: | ||
result = extractParam(node[0], defaultType, count) | ||
else: discard | ||
|
||
proc impl(node: NimNode): NimNode = | ||
const | ||
paramPos = 3 | ||
genericPos = 2 | ||
bodyPos = ^1 | ||
if node.kind in RoutineNodes: | ||
result = node | ||
else: | ||
result = newProc( | ||
procType = nnkLambda, | ||
body = node) | ||
result.copyLineInfo(node) | ||
let defaultType = | ||
if result.kind in {nnkTemplateDef, nnkMacroDef}: | ||
ident"untyped" | ||
else: | ||
ident"auto" | ||
if result[paramPos][0].kind == nnkEmpty: | ||
result[paramPos][0] = defaultType | ||
var paramCount = 0 | ||
if result[bodyPos].kind in {nnkStmtList, nnkStmtListExpr}: | ||
var i = 0 | ||
while i < result[bodyPos].len - 1: | ||
let e = result[bodyPos][i] | ||
let p = extractParam(e, defaultType, paramCount) | ||
if not p.isNil: | ||
result[paramPos].add(p) | ||
inc paramCount | ||
result[bodyPos].del(i) | ||
else: | ||
inc i | ||
else: | ||
let old = result[bodyPos] | ||
result[bodyPos] = newNimNode(nnkStmtListExpr, old) | ||
result[bodyPos].add(old) | ||
let | ||
body = result[bodyPos] | ||
callPos = body.len - 1 | ||
let call = body[callPos] | ||
case call.kind | ||
of nnkCallKinds, nnkObjConstr, nnkBracketExpr, nnkCurlyExpr, | ||
nnkPar, nnkTupleConstr, nnkBracket, nnkCurly: | ||
if call.kind in nnkCallKinds {nnkObjConstr}: | ||
let callee = call[0] | ||
if callee.kind == nnkBracketExpr: | ||
# maybe generic params | ||
var genericParams: seq[NimNode] | ||
var i = 1 | ||
while i < callee.len: | ||
let p = extractParam(callee[i], newEmptyNode(), paramCount) | ||
if not p.isNil: | ||
genericParams.add(p) | ||
inc paramCount | ||
callee.del(i) | ||
else: | ||
inc i | ||
if genericParams.len != 0: | ||
if result[genericPos].kind == nnkEmpty: | ||
result[genericPos] = newNimNode(nnkGenericParams, callee) | ||
result[genericPos].add(genericParams) | ||
if callee.len == 1: | ||
call[0] = callee[0] | ||
for i in 0 ..< call.len: | ||
let p = extractParam(call[i], defaultType, paramCount) | ||
if not p.isNil: | ||
result[paramPos].add(p) | ||
inc paramCount | ||
call[i] = p[0] | ||
if call.kind == nnkObjConstr and | ||
call.len > 1 and call[1].kind != nnkExprColonExpr: | ||
body[callPos] = newNimNode(nnkCall, call) | ||
for a in call: body[callPos].add(a) | ||
of nnkDotExpr, nnkDerefExpr: | ||
let p = extractParam(call[0], defaultType, paramCount) | ||
if not p.isNil: | ||
result[paramPos].add(p) | ||
inc paramCount | ||
call[0] = p[0] | ||
else: | ||
warning("unsupported deed node kind " & $body[callPos].kind, body[callPos]) | ||
|
||
macro deed*(node): untyped = | ||
result = impl(node) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1 @@ | ||
switch("path", "$projectDir/../src") |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 1,50 @@ | ||
when (compiles do: import nimbleutils/bridge): | ||
import nimbleutils/bridge | ||
else: | ||
import unittest | ||
import sequtils, algorithm | ||
|
||
import dirtydeeds | ||
|
||
test "basic cases": | ||
check @[1, 2, 3].map(deed _ * 7) == @[7, 14, 21] | ||
check @["A", "B", "C"].map(deed "foo" & (_: string)) == @["fooA", "fooB", "fooC"] | ||
check @['a', 'f', 'A', '0', 'c'].filter(deed contains({'a'..'z'}, _)) == @['a', 'f', 'c'] | ||
let a = deed (_: int) (_: int) | ||
check a(3, 4) == 7 | ||
proc foo[T](a, b: T, x: proc (a, b: T): T): T = x(a, b) | ||
proc foo[T](a: T, x: proc (a: T): T): T = x(a) | ||
check foo(3, 4, deed _ _) == 7 | ||
let max0 = deed max(0, _: int) | ||
let max0left = deed max(_: int, 0) | ||
check max0(7) == 7 | ||
check max0(-7) == 0 | ||
check max0left(7) == 7 | ||
check max0left(-7) == 0 | ||
check foo(7, deed max(0, _)) == 7 | ||
check foo(-7, deed max(0, _)) == 0 | ||
check foo(7, deed max(_, 0)) == 7 | ||
check foo(-7, deed max(_, 0)) == 0 | ||
let b = deed (_(a): int) a | ||
check b(12) == 24 | ||
check foo(7, deed _(a) a * 2) == 21 | ||
var s = @[5, 3, 4, 1, 9, 2] | ||
s.sort(deed _ - _) | ||
check s == @[1, 2, 3, 4, 5, 9] | ||
s.sort(deed (_ a; _ b; b - a)) | ||
check s == @[9, 5, 4, 3, 2, 1] | ||
let maxDefault0 = deed max(_: int, (_: int) = 0) | ||
check maxDefault0(7) == 7 | ||
check maxDefault0(-7) == 0 | ||
check maxDefault0(1, 7) == 7 | ||
check maxDefault0(-1, -7) == -1 | ||
|
||
test "declaration": | ||
proc foo {.deed.} = (_: string) & (_: char | string) | ||
check foo("abc", 'd') == "abcd" | ||
check foo("abc", "def") == "abcdef" | ||
proc bar {.deed.} = max[_ T](_: T, _: T) | ||
check bar(1, 2) == 2 | ||
check bar(-1, -2) == -1 | ||
# only works after 2.0 | ||
#template baz {.deed.} = toSeq _ |