diff --git a/CHANGES.md b/CHANGES.md
index c0fadbf799..295c930a63 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -15,6 +15,7 @@ Core Changes:
Language Improvements:
+- enh(zephir) almost complete rework of the zephir grammar (#2387) [Josh Goebel][]
- (markdown) much improved code block support (#2382) [Josh Goebel][]
- (markdown) improve bold/italic nesting (#2382) [Josh Goebel][]
- enh(csharp) Support `where` keyword as class constraint (#2378) [Josh Goebel][]
diff --git a/src/languages/zephir.js b/src/languages/zephir.js
index 45d74075ca..96635280f3 100644
--- a/src/languages/zephir.js
+++ b/src/languages/zephir.js
@@ -10,33 +10,44 @@ function(hljs) {
className: 'string',
contains: [hljs.BACKSLASH_ESCAPE],
variants: [
- {
- begin: 'b"', end: '"'
- },
- {
- begin: 'b\'', end: '\''
- },
hljs.inherit(hljs.APOS_STRING_MODE, {illegal: null}),
hljs.inherit(hljs.QUOTE_STRING_MODE, {illegal: null})
]
};
+ var TITLE_MODE = hljs.UNDERSCORE_TITLE_MODE;
var NUMBER = {variants: [hljs.BINARY_NUMBER_MODE, hljs.C_NUMBER_MODE]};
+ var KEYWORDS =
+ // classes and objects
+ 'namespace class interface use extends ' +
+ 'function return ' +
+ 'abstract final public protected private static deprecated ' +
+ // error handling
+ 'throw try catch Exception ' +
+ // keyword-ish things their website does NOT seem to highlight (in their own snippets)
+ // 'typeof fetch in ' +
+ // operators/helpers
+ 'echo empty isset instanceof unset ' +
+ // assignment/variables
+ 'let var new const self ' +
+ // control
+ 'require ' +
+ 'if else elseif switch case default ' +
+ 'do while loop for continue break ' +
+ 'likely unlikely ' +
+ // magic constants
+ // https://github.com/phalcon/zephir/blob/master/Library/Expression/Constants.php
+ '__LINE__ __FILE__ __DIR__ __FUNCTION__ __CLASS__ __TRAIT__ __METHOD__ __NAMESPACE__ ' +
+ // types - https://docs.zephir-lang.com/0.12/en/types
+ 'array boolean float double integer object resource string ' +
+ 'char long unsigned bool int uint ulong uchar ' +
+ // built-ins
+ 'true false null undefined';
+
return {
aliases: ['zep'],
- case_insensitive: true,
- keywords:
- 'and include_once list abstract global private echo interface as static endswitch ' +
- 'array null if endwhile or const for endforeach self var let while isset public ' +
- 'protected exit foreach throw elseif include __FILE__ empty require_once do xor ' +
- 'return parent clone use __CLASS__ __LINE__ else break print eval new ' +
- 'catch __METHOD__ case exception default die require __FUNCTION__ ' +
- 'enddeclare final try switch continue endfor endif declare unset true false ' +
- 'trait goto instanceof insteadof __DIR__ __NAMESPACE__ ' +
- 'yield finally int uint long ulong char uchar double float bool boolean string' +
- 'likely unlikely',
+ keywords: KEYWORDS,
contains: [
hljs.C_LINE_COMMENT_MODE,
- hljs.HASH_COMMENT_MODE,
hljs.COMMENT(
'/\\*',
'\\*/',
@@ -49,15 +60,6 @@ function(hljs) {
]
}
),
- hljs.COMMENT(
- '__halt_compiler.+?;',
- false,
- {
- endsWithParent: true,
- keywords: '__halt_compiler',
- lexemes: hljs.UNDERSCORE_IDENT_RE
- }
- ),
{
className: 'string',
begin: '<<<[\'"]?\\w+[\'"]?$', end: '^\\w+;',
@@ -69,13 +71,14 @@ function(hljs) {
},
{
className: 'function',
- beginKeywords: 'function', end: /[;{]/, excludeEnd: true,
+ beginKeywords: 'function fn', end: /[;{]/, excludeEnd: true,
illegal: '\\$|\\[|%',
contains: [
- hljs.UNDERSCORE_TITLE_MODE,
+ TITLE_MODE,
{
className: 'params',
begin: '\\(', end: '\\)',
+ keywords: KEYWORDS,
contains: [
'self',
hljs.C_BLOCK_COMMENT_MODE,
@@ -91,17 +94,17 @@ function(hljs) {
illegal: /[:\(\$"]/,
contains: [
{beginKeywords: 'extends implements'},
- hljs.UNDERSCORE_TITLE_MODE
+ TITLE_MODE
]
},
{
beginKeywords: 'namespace', end: ';',
illegal: /[\.']/,
- contains: [hljs.UNDERSCORE_TITLE_MODE]
+ contains: [TITLE_MODE]
},
{
beginKeywords: 'use', end: ';',
- contains: [hljs.UNDERSCORE_TITLE_MODE]
+ contains: [TITLE_MODE]
},
{
begin: '=>' // No markup, just a relevance booster
diff --git a/test/markup/zephir/default.expect.txt b/test/markup/zephir/default.expect.txt
new file mode 100644
index 0000000000..099d29299a
--- /dev/null
+++ b/test/markup/zephir/default.expect.txt
@@ -0,0 +1,55 @@
+function testBefore(<Test> a, var b = 5, int c = 10)
+{
+ a->method1();
+
+ return b + c;
+}
+
+namespace Test;
+
+use RuntimeException as RE;
+
+
+class Test extends CustomClass implements TestInterface
+{
+ const C1 = null;
+
+
+ const className = __CLASS__;
+
+ public function method1()
+ {
+ int a = 1, b = 2;
+ return a + b;
+ }
+
+
+ public fn method2() -> <Test>
+ {
+ call_user_func(function() { echo "hello"; });
+
+
+ [1, 2, 3, 4, 5]->walk(
+ function(int! x) {
+ return x * x;
+ }
+ );
+
+ [1, 2, 3, 4, 5]->walk(
+ function(_, int key) { echo key; }
+ );
+
+ array input = [1, 2, 3, 4, 5];
+
+ input->walk(
+ function(_, int key) { echo key; }
+ );
+
+
+ input->map(x => x * x);
+
+ return this;
+ }
+}
diff --git a/test/markup/zephir/default.txt b/test/markup/zephir/default.txt
new file mode 100644
index 0000000000..8142e7f10c
--- /dev/null
+++ b/test/markup/zephir/default.txt
@@ -0,0 +1,55 @@
+function testBefore( a, var b = 5, int c = 10)
+{
+ a->method1();
+
+ return b + c;
+}
+
+namespace Test;
+
+use RuntimeException as RE;
+
+/**
+ * Example comment
+ */
+class Test extends CustomClass implements TestInterface
+{
+ const C1 = null;
+
+ // Magic constant: http://php.net/manual/ru/language.constants.predefined.php
+ const className = __CLASS__;
+
+ public function method1()
+ {
+ int a = 1, b = 2;
+ return a + b;
+ }
+
+ // See fn is allowed like shortcut
+ public fn method2() ->
+ {
+ call_user_func(function() { echo "hello"; });
+
+
+ [1, 2, 3, 4, 5]->walk(
+ function(int! x) {
+ return x * x;
+ }
+ );
+
+ [1, 2, 3, 4, 5]->walk(
+ function(_, int key) { echo key; }
+ );
+
+ array input = [1, 2, 3, 4, 5];
+
+ input->walk(
+ function(_, int key) { echo key; }
+ );
+
+
+ input->map(x => x * x);
+
+ return this;
+ }
+}