diff --git a/src/parser.pegjs b/src/parser.pegjs
index 93b53c855341987f1d01e43b3e27c3a271a81228..eccf5f23306d78455a24fd7a3f2c58f3678584e4 100644
--- a/src/parser.pegjs
+++ b/src/parser.pegjs
@@ -92,8 +92,9 @@ full
 	/ mention
 	/ hashtag
 	/ url
+	/ fnVer2
 	/ link
-	/ fn
+	/ fnVer1
 	/ search // block
 	/ inlineText
 
@@ -110,8 +111,9 @@ inline
 	/ mention
 	/ hashtag
 	/ url
+	/ fnVer2
 	/ link
-	/ fn
+	/ fnVer1
 	/ inlineText
 
 //
@@ -392,13 +394,20 @@ linkUrl
 
 // inline: fn
 
-fn
+fnVer1
 	= "[" name:$([a-z0-9_]i)+ args:fnArgs? _ content:(!"]" i:inline { return i; })+ "]"
 {
 	args = args || {};
 	return FN(name, args, mergeText(content));
 }
 
+fnVer2
+	= "$[" name:$([a-z0-9_]i)+ args:fnArgs? _ content:(!"]" i:inline { return i; })+ "]"
+{
+	args = args || {};
+	return FN(name, args, mergeText(content));
+}
+
 fnArgs
 	= "." head:fnArg tails:("," arg:fnArg { return arg; })*
 {
diff --git a/test/parser.ts b/test/parser.ts
index 908818632b6862af7d9d21c802022a0281af880e..b3e7f8fdd7ab4976f5b14d61ee004bd976e8811a 100644
--- a/test/parser.ts
+++ b/test/parser.ts
@@ -621,7 +621,7 @@ describe('FullParser', () => {
 		});
 	});
 
-	describe('fn', () => {
+	describe('fn v1', () => {
 		it('basic', () => {
 			const input = '[tada abc]';
 			const output = [
@@ -641,6 +641,52 @@ describe('FullParser', () => {
 			];
 			assert.deepStrictEqual(mfm.parse(input), output);
 		});
+
+		it('nest', () => {
+			const input = '[spin.speed=1.1s [shake a]]';
+			const output = [
+				FN('spin', { speed: '1.1s' }, [
+					FN('shake', { }, [
+						TEXT('a')
+					])
+				])
+			];
+			assert.deepStrictEqual(mfm.parse(input), output);
+		});
+	});
+
+	describe('fn v2', () => {
+		it('basic', () => {
+			const input = '$[tada abc]';
+			const output = [
+				FN('tada', { }, [
+					TEXT('abc')
+				])
+			];
+			assert.deepStrictEqual(mfm.parse(input), output);
+		});
+
+		it('with a string argument', () => {
+			const input = '$[spin.speed=1.1s a]';
+			const output = [
+				FN('spin', { speed: '1.1s' }, [
+					TEXT('a')
+				])
+			];
+			assert.deepStrictEqual(mfm.parse(input), output);
+		});
+
+		it('nest', () => {
+			const input = '$[spin.speed=1.1s $[shake a]]';
+			const output = [
+				FN('spin', { speed: '1.1s' }, [
+					FN('shake', { }, [
+						TEXT('a')
+					])
+				])
+			];
+			assert.deepStrictEqual(mfm.parse(input), output);
+		});
 	});
 
 	it('composite', () => {