diff --git a/lib/linguist/languages.yml b/lib/linguist/languages.yml
index 626786e9..6d02c1d0 100644
--- a/lib/linguist/languages.yml
+++ b/lib/linguist/languages.yml
@@ -2032,6 +2032,12 @@ YAML:
- .rviz
- .yaml
+Zephir:
+ type: programming
+ lexer: PHP
+ color: "#118f9e"
+ primary_extension: .zep
+
eC:
type: programming
search_term: ec
diff --git a/lib/linguist/samples.json b/lib/linguist/samples.json
index 9864f7a1..5ed874d7 100644
--- a/lib/linguist/samples.json
+++ b/lib/linguist/samples.json
@@ -6,6 +6,9 @@
"Agda": [
".agda"
],
+ "Alloy": [
+ ".als"
+ ],
"Apex": [
".cls"
],
@@ -449,6 +452,9 @@
".sh",
".zsh"
],
+ "ShellSession": [
+ ".sh-session"
+ ],
"Shen": [
".shen"
],
@@ -537,6 +543,9 @@
],
"YAML": [
".yml"
+ ],
+ "Zephir": [
+ ".zep"
]
},
"interpreters": {
@@ -600,8 +609,8 @@
".gemrc"
]
},
- "tokens_total": 525524,
- "languages_total": 635,
+ "tokens_total": 527926,
+ "languages_total": 643,
"tokens": {
"ABAP": {
"*/**": 1,
@@ -920,6 +929,241 @@
"zero": 1,
"Nat": 1
},
+ "Alloy": {
+ "module": 3,
+ "examples/systems/file_system": 1,
+ "abstract": 2,
+ "sig": 20,
+ "Object": 10,
+ "{": 54,
+ "}": 60,
+ "Name": 2,
+ "File": 1,
+ "extends": 10,
+ "some": 3,
+ "d": 3,
+ "Dir": 8,
+ "|": 19,
+ "this": 14,
+ "in": 19,
+ "d.entries.contents": 1,
+ "entries": 3,
+ "set": 10,
+ "DirEntry": 2,
+ "parent": 3,
+ "lone": 6,
+ "this.": 4,
+ "@contents.": 1,
+ "@entries": 1,
+ "all": 16,
+ "e1": 2,
+ "e2": 2,
+ "e1.name": 1,
+ "e2.name": 1,
+ "@parent": 2,
+ "Root": 5,
+ "one": 8,
+ "no": 8,
+ "Cur": 1,
+ "name": 1,
+ "contents": 2,
+ "pred": 16,
+ "OneParent_buggyVersion": 2,
+ "-": 41,
+ "d.parent": 2,
+ "OneParent_correctVersion": 2,
+ "(": 12,
+ "&&": 2,
+ "contents.d": 1,
+ ")": 9,
+ "NoDirAliases": 3,
+ "o": 1,
+ "o.": 1,
+ "check": 6,
+ "for": 7,
+ "expect": 6,
+ "examples/systems/marksweepgc": 1,
+ "Node": 10,
+ "HeapState": 5,
+ "left": 3,
+ "right": 1,
+ "marked": 1,
+ "freeList": 1,
+ "clearMarks": 1,
+ "[": 82,
+ "hs": 16,
+ ".marked": 3,
+ ".right": 4,
+ "hs.right": 3,
+ "fun": 1,
+ "reachable": 1,
+ "n": 5,
+ "]": 80,
+ "+": 14,
+ "n.": 1,
+ "hs.left": 2,
+ "mark": 1,
+ "from": 2,
+ "hs.reachable": 1,
+ "setFreeList": 1,
+ ".freeList.*": 3,
+ ".left": 5,
+ "hs.marked": 1,
+ "GC": 1,
+ "root": 5,
+ "assert": 3,
+ "Soundness1": 2,
+ "h": 9,
+ "live": 3,
+ "h.reachable": 1,
+ "h.right": 1,
+ "Soundness2": 2,
+ ".reachable": 2,
+ "h.GC": 1,
+ ".freeList": 1,
+ "Completeness": 1,
+ "examples/systems/views": 1,
+ "open": 2,
+ "util/ordering": 1,
+ "State": 16,
+ "as": 2,
+ "so": 1,
+ "util/relation": 1,
+ "rel": 1,
+ "Ref": 19,
+ "t": 16,
+ "b": 13,
+ "v": 25,
+ "views": 2,
+ "when": 1,
+ "is": 1,
+ "view": 2,
+ "of": 3,
+ "type": 1,
+ "backing": 1,
+ "dirty": 3,
+ "contains": 1,
+ "refs": 7,
+ "that": 1,
+ "have": 1,
+ "been": 1,
+ "invalidated": 1,
+ "obj": 1,
+ "ViewType": 8,
+ "anyviews": 2,
+ "visualization": 1,
+ "ViewType.views": 1,
+ "Map": 2,
+ "keys": 3,
+ "map": 2,
+ "s": 6,
+ "Ref.map": 1,
+ "s.refs": 3,
+ "MapRef": 4,
+ "fact": 4,
+ "State.obj": 3,
+ "Iterator": 2,
+ "done": 3,
+ "lastRef": 2,
+ "IteratorRef": 5,
+ "Set": 2,
+ "elts": 2,
+ "SetRef": 5,
+ "KeySetView": 6,
+ "State.views": 1,
+ "IteratorView": 3,
+ "s.views": 2,
+ "handle": 1,
+ "possibility": 1,
+ "modifying": 1,
+ "an": 1,
+ "object": 1,
+ "and": 1,
+ "its": 1,
+ "at": 1,
+ "once": 1,
+ "*": 1,
+ "should": 1,
+ "we": 1,
+ "limit": 1,
+ "frame": 1,
+ "conds": 1,
+ "to": 1,
+ "non": 1,
+ "*/": 1,
+ "modifies": 5,
+ "pre": 15,
+ "post": 14,
+ "rs": 4,
+ "let": 5,
+ "vr": 1,
+ "pre.views": 8,
+ "mods": 3,
+ "rs.*vr": 1,
+ "r": 3,
+ "pre.refs": 6,
+ "pre.obj": 10,
+ "post.obj": 7,
+ "viewFrame": 4,
+ "post.dirty": 1,
+ "pre.dirty": 1,
+ "allocates": 5,
+ "&": 3,
+ "post.refs": 1,
+ ".map": 3,
+ ".elts": 3,
+ "dom": 1,
+ "<:>": 1,
+ "setRefs": 1,
+ "MapRef.put": 1,
+ "k": 5,
+ "none": 4,
+ "post.views": 4,
+ "SetRef.iterator": 1,
+ "iterRef": 4,
+ "i": 7,
+ "i.left": 3,
+ "i.done": 1,
+ "i.lastRef": 1,
+ "IteratorRef.remove": 1,
+ ".lastRef": 2,
+ "IteratorRef.next": 1,
+ "ref": 3,
+ "IteratorRef.hasNext": 1,
+ "s.obj": 1,
+ "zippishOK": 2,
+ "ks": 6,
+ "vs": 6,
+ "m": 4,
+ "ki": 2,
+ "vi": 2,
+ "s0": 4,
+ "so/first": 1,
+ "s1": 4,
+ "so/next": 7,
+ "s2": 6,
+ "s3": 4,
+ "s4": 4,
+ "s5": 4,
+ "s6": 4,
+ "s7": 2,
+ "precondition": 2,
+ "s0.dirty": 1,
+ "ks.iterator": 1,
+ "vs.iterator": 1,
+ "ki.hasNext": 1,
+ "vi.hasNext": 1,
+ "ki.this/next": 1,
+ "vi.this/next": 1,
+ "m.put": 1,
+ "ki.remove": 1,
+ "vi.remove": 1,
+ "State.dirty": 1,
+ "ViewType.pre.views": 2,
+ "but": 1,
+ "#s.obj": 1,
+ "<": 1
+ },
"ApacheConf": {
"ServerSignature": 1,
"Off": 1,
@@ -48819,6 +49063,151 @@
"foodforthought.jpg": 1,
"name##*fo": 1
},
+ "ShellSession": {
+ "echo": 2,
+ "FOOBAR": 2,
+ "Hello": 2,
+ "World": 2,
+ "gem": 4,
+ "install": 4,
+ "nokogiri": 6,
+ "...": 4,
+ "Building": 2,
+ "native": 2,
+ "extensions.": 2,
+ "This": 4,
+ "could": 2,
+ "take": 2,
+ "a": 4,
+ "while...": 2,
+ "checking": 1,
+ "for": 4,
+ "libxml/parser.h...": 1,
+ "***": 2,
+ "extconf.rb": 1,
+ "failed": 1,
+ "Could": 2,
+ "not": 2,
+ "create": 1,
+ "Makefile": 1,
+ "due": 1,
+ "to": 3,
+ "some": 1,
+ "reason": 2,
+ "probably": 1,
+ "lack": 1,
+ "of": 2,
+ "necessary": 1,
+ "libraries": 1,
+ "and/or": 1,
+ "headers.": 1,
+ "Check": 1,
+ "the": 2,
+ "mkmf.log": 1,
+ "file": 1,
+ "more": 1,
+ "details.": 1,
+ "You": 1,
+ "may": 1,
+ "need": 1,
+ "configuration": 1,
+ "options.": 1,
+ "brew": 2,
+ "tap": 2,
+ "homebrew/dupes": 1,
+ "Cloning": 1,
+ "into": 1,
+ "remote": 3,
+ "Counting": 1,
+ "objects": 3,
+ "done.": 4,
+ "Compressing": 1,
+ "%": 5,
+ "(": 6,
+ "/591": 1,
+ ")": 6,
+ "Total": 1,
+ "delta": 2,
+ "reused": 1,
+ "Receiving": 1,
+ "/1034": 1,
+ "KiB": 1,
+ "|": 1,
+ "bytes/s": 1,
+ "Resolving": 1,
+ "deltas": 1,
+ "/560": 1,
+ "Checking": 1,
+ "connectivity...": 1,
+ "done": 1,
+ "Warning": 1,
+ "homebrew/dupes/lsof": 1,
+ "over": 1,
+ "mxcl/master/lsof": 1,
+ "Tapped": 1,
+ "formula": 4,
+ "apple": 1,
+ "-": 12,
+ "gcc42": 1,
+ "Downloading": 1,
+ "http": 2,
+ "//r.research.att.com/tools/gcc": 1,
+ "darwin11.pkg": 1,
+ "########################################################################": 1,
+ "Caveats": 1,
+ "NOTE": 1,
+ "provides": 1,
+ "components": 1,
+ "that": 1,
+ "were": 1,
+ "removed": 1,
+ "from": 3,
+ "XCode": 2,
+ "in": 2,
+ "release.": 1,
+ "There": 1,
+ "is": 2,
+ "no": 1,
+ "this": 1,
+ "if": 1,
+ "you": 1,
+ "are": 1,
+ "using": 1,
+ "version": 1,
+ "prior": 1,
+ "contains": 1,
+ "compilers": 2,
+ "built": 2,
+ "Apple": 1,
+ "s": 1,
+ "GCC": 1,
+ "sources": 1,
+ "build": 1,
+ "available": 1,
+ "//opensource.apple.com/tarballs/gcc": 1,
+ "All": 1,
+ "have": 1,
+ "suffix.": 1,
+ "A": 1,
+ "GFortran": 1,
+ "compiler": 1,
+ "also": 1,
+ "included.": 1,
+ "Summary": 1,
+ "/usr/local/Cellar/apple": 1,
+ "gcc42/4.2.1": 1,
+ "files": 1,
+ "M": 1,
+ "seconds": 1,
+ "v": 1,
+ "Fetching": 1,
+ "Successfully": 1,
+ "installed": 2,
+ "Installing": 2,
+ "ri": 1,
+ "documentation": 2,
+ "RDoc": 1
+ },
"Shen": {
"*": 47,
"graph.shen": 1,
@@ -54312,11 +54701,163 @@
"GMT": 1,
"recorded_with": 1,
"VCR": 1
+ },
+ "Zephir": {
+ "%": 10,
+ "{": 56,
+ "#define": 1,
+ "MAX_FACTOR": 3,
+ "}": 50,
+ "namespace": 3,
+ "Test": 2,
+ ";": 86,
+ "#include": 1,
+ "static": 1,
+ "long": 3,
+ "fibonacci": 4,
+ "(": 55,
+ "n": 5,
+ ")": 53,
+ "if": 39,
+ "<": 1,
+ "return": 25,
+ "else": 11,
+ "-": 25,
+ "+": 5,
+ "class": 2,
+ "Cblock": 1,
+ "public": 22,
+ "function": 22,
+ "testCblock1": 1,
+ "int": 3,
+ "a": 6,
+ "testCblock2": 1,
+ "Router": 1,
+ "Route": 1,
+ "protected": 9,
+ "_pattern": 3,
+ "_compiledPattern": 3,
+ "_paths": 3,
+ "_methods": 5,
+ "_hostname": 3,
+ "_converters": 3,
+ "_id": 2,
+ "_name": 3,
+ "_beforeMatch": 3,
+ "__construct": 1,
+ "pattern": 37,
+ "paths": 7,
+ "null": 11,
+ "httpMethods": 6,
+ "this": 28,
+ "reConfigure": 2,
+ "let": 51,
+ "compilePattern": 2,
+ "var": 4,
+ "idPattern": 6,
+ "memstr": 10,
+ "str_replace": 6,
+ ".": 5,
+ "via": 1,
+ "extractNamedParams": 2,
+ "string": 6,
+ "char": 1,
+ "ch": 27,
+ "tmp": 4,
+ "matches": 5,
+ "boolean": 1,
+ "notValid": 5,
+ "false": 3,
+ "cursor": 4,
+ "cursorVar": 5,
+ "marker": 4,
+ "bracketCount": 7,
+ "parenthesesCount": 5,
+ "foundPattern": 6,
+ "intermediate": 4,
+ "numberMatches": 4,
+ "route": 12,
+ "item": 7,
+ "variable": 5,
+ "regexp": 7,
+ "strlen": 1,
+ "<=>": 5,
+ "0": 9,
+ "for": 4,
+ "in": 4,
+ "1": 3,
+ "substr": 3,
+ "break": 9,
+ "&&": 6,
+ "z": 2,
+ "Z": 2,
+ "true": 2,
+ "<='9')>": 1,
+ "_": 1,
+ "2": 2,
+ "continue": 1,
+ "[": 14,
+ "]": 14,
+ "moduleName": 5,
+ "controllerName": 7,
+ "actionName": 4,
+ "parts": 9,
+ "routePaths": 5,
+ "realClassName": 1,
+ "namespaceName": 1,
+ "pcrePattern": 4,
+ "compiledPattern": 4,
+ "extracted": 4,
+ "typeof": 2,
+ "throw": 1,
+ "new": 1,
+ "Exception": 1,
+ "explode": 1,
+ "switch": 1,
+ "count": 1,
+ "case": 3,
+ "controller": 1,
+ "action": 1,
+ "array": 1,
+ "The": 1,
+ "contains": 1,
+ "invalid": 1,
+ "#": 1,
+ "array_merge": 1,
+ "//Update": 1,
+ "the": 1,
+ "s": 1,
+ "name": 5,
+ "*": 2,
+ "@return": 1,
+ "*/": 1,
+ "getName": 1,
+ "setName": 1,
+ "beforeMatch": 1,
+ "callback": 2,
+ "getBeforeMatch": 1,
+ "getRouteId": 1,
+ "getPattern": 1,
+ "getCompiledPattern": 1,
+ "getPaths": 1,
+ "getReversedPaths": 1,
+ "reversed": 4,
+ "path": 3,
+ "position": 3,
+ "setHttpMethods": 1,
+ "getHttpMethods": 1,
+ "setHostname": 1,
+ "hostname": 2,
+ "getHostname": 1,
+ "convert": 1,
+ "converter": 2,
+ "getConverters": 1
}
},
"language_tokens": {
"ABAP": 1500,
"Agda": 376,
+ "Alloy": 1143,
"ApacheConf": 1449,
"Apex": 4408,
"AppleScript": 1862,
@@ -54444,6 +54985,7 @@
"Scilab": 69,
"SCSS": 39,
"Shell": 3744,
+ "ShellSession": 233,
"Shen": 3472,
"Slash": 187,
"SourcePawn": 2080,
@@ -54471,11 +55013,13 @@
"XQuery": 801,
"XSLT": 44,
"Xtend": 399,
- "YAML": 77
+ "YAML": 77,
+ "Zephir": 1026
},
"languages": {
"ABAP": 1,
"Agda": 1,
+ "Alloy": 3,
"ApacheConf": 3,
"Apex": 6,
"AppleScript": 7,
@@ -54603,6 +55147,7 @@
"Scilab": 3,
"SCSS": 1,
"Shell": 37,
+ "ShellSession": 3,
"Shen": 3,
"Slash": 1,
"SourcePawn": 1,
@@ -54630,7 +55175,8 @@
"XQuery": 1,
"XSLT": 1,
"Xtend": 2,
- "YAML": 2
+ "YAML": 2,
+ "Zephir": 2
},
- "md5": "bea2304b61e5920c044b83aec7788dd8"
+ "md5": "37a50c87400d09dbc9cc99f1276d7e33"
}
\ No newline at end of file
diff --git a/samples/Zephir/Cblock.zep b/samples/Zephir/Cblock.zep
new file mode 100644
index 00000000..43db8d7a
--- /dev/null
+++ b/samples/Zephir/Cblock.zep
@@ -0,0 +1,53 @@
+/**
+ * CBLOCK tests
+ * asfas
+ */
+
+%{
+// top statement before namespace, add to after headers
+#define MAX_FACTOR 40
+}%
+
+namespace Test;
+
+%{
+// top statement before class, add to after headers
+// test include .h
+#include "kernel/require.h"
+}%
+
+%{
+
+// c implement fibonacci
+static long fibonacci(long n) {
+ if (n < 2) return n;
+ else return fibonacci(n - 2) + fibonacci(n - 1);
+}
+
+}%
+
+class Cblock
+{
+ public function testCblock1()
+ {
+ int a = 0;
+
+ %{
+ a = MAX_FACTOR;
+ }%
+
+ return a;
+ }
+
+ public function testCblock2()
+ {
+ long a = 0;
+
+ %{
+ a = fibonacci(MAX_FACTOR);
+ }%
+
+ return a;
+ }
+
+}
diff --git a/samples/Zephir/Router.zep b/samples/Zephir/Router.zep
new file mode 100644
index 00000000..a8d4f907
--- /dev/null
+++ b/samples/Zephir/Router.zep
@@ -0,0 +1,557 @@
+
+namespace Test\Router;
+
+/**
+ * Test\Router\Route
+ *
+ * This class represents every route added to the router
+ */
+class Route
+{
+ protected _pattern;
+
+ protected _compiledPattern;
+
+ protected _paths;
+
+ protected _methods;
+
+ protected _hostname;
+
+ protected _converters;
+
+ protected _id;
+
+ protected _name;
+
+ protected _beforeMatch;
+
+ /**
+ * Test\Router\Route constructor
+ *
+ * @param string pattern
+ * @param array paths
+ * @param array|string httpMethods
+ */
+ public function __construct(pattern, paths=null, httpMethods=null)
+ {
+ // Configure the route (extract parameters, paths, etc)
+ this->reConfigure(pattern, paths);
+
+ // Update the HTTP method constraints
+ let this->_methods = httpMethods;
+ }
+
+ /**
+ * Replaces placeholders from pattern returning a valid PCRE regular expression
+ *
+ * @param string pattern
+ * @return string
+ */
+ public function compilePattern(pattern)
+ {
+ var idPattern;
+
+ // If a pattern contains ':', maybe there are placeholders to replace
+ if memstr(pattern, ":") {
+
+ // This is a pattern for valid identifiers
+ let idPattern = "/([a-zA-Z0-9\\_\\-]+)";
+
+ // Replace the module part
+ if memstr(pattern, "/:module") {
+ let pattern = str_replace("/:module", idPattern, pattern);
+ }
+
+ // Replace the controller placeholder
+ if memstr(pattern, "/:controller") {
+ let pattern = str_replace("/:controller", idPattern, pattern);
+ }
+
+ // Replace the namespace placeholder
+ if memstr(pattern, "/:namespace") {
+ let pattern = str_replace("/:namespace", idPattern, pattern);
+ }
+
+ // Replace the action placeholder
+ if memstr(pattern, "/:action") {
+ let pattern = str_replace("/:action", idPattern, pattern);
+ }
+
+ // Replace the params placeholder
+ if memstr(pattern, "/:params") {
+ let pattern = str_replace("/:params", "(/.*)*", pattern);
+ }
+
+ // Replace the int placeholder
+ if memstr(pattern, "/:int") {
+ let pattern = str_replace("/:int", "/([0-9]+)", pattern);
+ }
+ }
+
+ // Check if the pattern has parantheses in order to add the regex delimiters
+ if memstr(pattern, "(") {
+ return "#^" . pattern . "$#";
+ }
+
+ // Square brackets are also checked
+ if memstr(pattern, "[") {
+ return "#^" . pattern . "$#";
+ }
+
+ return pattern;
+ }
+
+ /**
+ * Set one or more HTTP methods that constraint the matching of the route
+ *
+ *
+ * $route->via('GET');
+ * $route->via(array('GET', 'POST'));
+ *
+ *
+ * @param string|array httpMethods
+ * @return Test\Router\Route
+ */
+ public function via(httpMethods)
+ {
+ let this->_methods = httpMethods;
+ return this;
+ }
+
+ /**
+ * Extracts parameters from a string
+ *
+ * @param string pattern
+ */
+ public function extractNamedParams(string pattern)
+ {
+
+ char ch;
+ var tmp, matches;
+ boolean notValid = false;
+ int cursor, cursorVar, marker, bracketCount = 0, parenthesesCount = 0, foundPattern = 0;
+ int intermediate = 0, numberMatches = 0;
+ string route, item, variable, regexp;
+
+ if strlen(pattern) <= 0 {
+ return false;
+ }
+
+ let matches = [],
+ route = "";
+
+ for cursor, ch in pattern {
+
+ if parenthesesCount == 0 {
+ if ch == '{' {
+ if bracketCount == 0 {
+ let marker = cursor + 1,
+ intermediate = 0,
+ notValid = false;
+ }
+ let bracketCount++;
+ } else {
+ if ch == '}' {
+ let bracketCount--;
+ if intermediate > 0 {
+ if bracketCount == 0 {
+
+ let numberMatches++,
+ variable = null,
+ regexp = null,
+ item = (string) substr(pattern, marker, cursor - marker);
+
+ for cursorVar, ch in item {
+
+ if ch == '\0' {
+ break;
+ }
+
+ if cursorVar == 0 && !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+ let notValid = true;
+ break;
+ }
+
+ if (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <='9') || ch == '-' || ch == '_' || ch == ':' {
+ if ch == ':' {
+ let variable = (string) substr(item, 0, cursorVar),
+ regexp = (string) substr(item, cursorVar + 1);
+ break;
+ }
+ } else {
+ let notValid = true;
+ break;
+ }
+
+ }
+
+ if !notValid {
+
+ let tmp = numberMatches;
+
+ if variable && regexp {
+
+ let foundPattern = 0;
+ for ch in regexp {
+ if ch == '\0' {
+ break;
+ }
+ if !foundPattern {
+ if ch == '(' {
+ let foundPattern = 1;
+ }
+ } else {
+ if ch == ')' {
+ let foundPattern = 2;
+ break;
+ }
+ }
+ }
+
+ if foundPattern != 2 {
+ let route .= '(',
+ route .= regexp,
+ route .= ')';
+ } else {
+ let route .= regexp;
+ }
+ let matches[variable] = tmp;
+ } else {
+ let route .= "([^/]*)",
+ matches[item] = tmp;
+ }
+ } else {
+ let route .= "{" . item ."}";
+ }
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ if bracketCount == 0 {
+ if ch == '(' {
+ let parenthesesCount++;
+ } else {
+ if ch == ')' {
+ let parenthesesCount--;
+ if parenthesesCount == 0 {
+ let numberMatches++;
+ }
+ }
+ }
+ }
+
+ if bracketCount > 0 {
+ let intermediate++;
+ } else {
+ let route .= ch;
+ }
+ }
+
+ return [route, matches];
+ }
+
+ /**
+ * Reconfigure the route adding a new pattern and a set of paths
+ *
+ * @param string pattern
+ * @param array paths
+ */
+ public function reConfigure(pattern, paths=null)
+ {
+ var moduleName, controllerName, actionName,
+ parts, routePaths, realClassName, namespaceName,
+ pcrePattern, compiledPattern, extracted;
+
+ if typeof pattern != "string" {
+ throw new Exception("The pattern must be string");
+ }
+
+ if paths !== null {
+ if typeof paths == "string" {
+
+ let moduleName = null,
+ controllerName = null,
+ actionName = null;
+
+ // Explode the short paths using the :: separator
+ let parts = explode("::", paths);
+
+ // Create the array paths dynamically
+ switch count(parts) {
+ case 3:
+ let moduleName = parts[0],
+ controllerName = parts[1],
+ actionName = parts[2];
+ break;
+ case 2:
+ let controllerName = parts[0],
+ actionName = parts[1];
+ break;
+ case 1:
+ let controllerName = parts[0];
+ break;
+ }
+
+ let routePaths = [];
+
+ // Process module name
+ if moduleName !== null {
+ let routePaths["module"] = moduleName;
+ }
+
+ // Process controller name
+ if controllerName !== null {
+
+ // Check if we need to obtain the namespace
+ if memstr(controllerName, "\\") {
+
+ // Extract the real class name from the namespaced class
+ let realClassName = get_class_ns(controllerName);
+
+ // Extract the namespace from the namespaced class
+ let namespaceName = get_ns_class(controllerName);
+
+ // Update the namespace
+ if namespaceName {
+ let routePaths["namespace"] = namespaceName;
+ }
+ } else {
+ let realClassName = controllerName;
+ }
+
+ // Always pass the controller to lowercase
+ let routePaths["controller"] = uncamelize(realClassName);
+ }
+
+ // Process action name
+ if actionName !== null {
+ let routePaths["action"] = actionName;
+ }
+ } else {
+ let routePaths = paths;
+ }
+ } else {
+ let routePaths = [];
+ }
+
+ if typeof routePaths !== "array" {
+ throw new Exception("The route contains invalid paths");
+ }
+
+ // If the route starts with '#' we assume that it is a regular expression
+ if !starts_with(pattern, "#") {
+
+ if memstr(pattern, "{") {
+ // The route has named parameters so we need to extract them
+ let extracted = this->extractNamedParams(pattern),
+ pcrePattern = extracted[0],
+ routePaths = array_merge(routePaths, extracted[1]);
+ } else {
+ let pcrePattern = pattern;
+ }
+
+ // Transform the route's pattern to a regular expression
+ let compiledPattern = this->compilePattern(pcrePattern);
+ } else {
+ let compiledPattern = pattern;
+ }
+
+ // Update the original pattern
+ let this->_pattern = pattern;
+
+ // Update the compiled pattern
+ let this->_compiledPattern = compiledPattern;
+
+ //Update the route's paths
+ let this->_paths = routePaths;
+ }
+
+ /**
+ * Returns the route's name
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return this->_name;
+ }
+
+ /**
+ * Sets the route's name
+ *
+ *
+ * $router->add('/about', array(
+ * 'controller' => 'about'
+ * ))->setName('about');
+ *
+ *
+ * @param string name
+ * @return Route
+ */
+ public function setName(name)
+ {
+ let this->_name = name;
+ return this;
+ }
+
+ /**
+ * Sets a callback that is called if the route is matched.
+ * The developer can implement any arbitrary conditions here
+ * If the callback returns false the route is treaded as not matched
+ *
+ * @param callback callback
+ * @return Test\Router\Route
+ */
+ public function beforeMatch(callback)
+ {
+ let this->_beforeMatch = callback;
+ return this;
+ }
+
+ /**
+ * Returns the 'before match' callback if any
+ *
+ * @return mixed
+ */
+ public function getBeforeMatch()
+ {
+ return this->_beforeMatch;
+ }
+
+ /**
+ * Returns the route's id
+ *
+ * @return string
+ */
+ public function getRouteId()
+ {
+ return this->_id;
+ }
+
+ /**
+ * Returns the route's pattern
+ *
+ * @return string
+ */
+ public function getPattern()
+ {
+ return this->_pattern;
+ }
+
+ /**
+ * Returns the route's compiled pattern
+ *
+ * @return string
+ */
+ public function getCompiledPattern()
+ {
+ return this->_compiledPattern;
+ }
+
+ /**
+ * Returns the paths
+ *
+ * @return array
+ */
+ public function getPaths()
+ {
+ return this->_paths;
+ }
+
+ /**
+ * Returns the paths using positions as keys and names as values
+ *
+ * @return array
+ */
+ public function getReversedPaths()
+ {
+ var reversed, path, position;
+
+ let reversed = [];
+ for path, position in this->_paths {
+ let reversed[position] = path;
+ }
+ return reversed;
+ }
+
+ /**
+ * Sets a set of HTTP methods that constraint the matching of the route (alias of via)
+ *
+ *
+ * $route->setHttpMethods('GET');
+ * $route->setHttpMethods(array('GET', 'POST'));
+ *
+ *
+ * @param string|array httpMethods
+ * @return Test\Router\Route
+ */
+ public function setHttpMethods(httpMethods)
+ {
+ let this->_methods = httpMethods;
+ return this;
+ }
+
+ /**
+ * Returns the HTTP methods that constraint matching the route
+ *
+ * @return string|array
+ */
+ public function getHttpMethods()
+ {
+ return this->_methods;
+ }
+
+ /**
+ * Sets a hostname restriction to the route
+ *
+ *
+ * $route->setHostname('localhost');
+ *
+ *
+ * @param string|array httpMethods
+ * @return Test\Router\Route
+ */
+ public function setHostname(hostname)
+ {
+ let this->_hostname = hostname;
+ return this;
+ }
+
+ /**
+ * Returns the hostname restriction if any
+ *
+ * @return string
+ */
+ public function getHostname()
+ {
+ return this->_hostname;
+ }
+
+ /**
+ * Adds a converter to perform an additional transformation for certain parameter
+ *
+ * @param string name
+ * @param callable converter
+ * @return Test\Router\Route
+ */
+ public function convert(name, converter)
+ {
+ let this->_converters[name] = converter;
+ return this;
+ }
+
+ /**
+ * Returns the router converter
+ *
+ * @return array
+ */
+ public function getConverters()
+ {
+ return this->_converters;
+ }
+
+}