diff --git a/syntaxes/csharp-new.json b/syntaxes/csharp-new.json index 10b3dcf..c4854af 100644 --- a/syntaxes/csharp-new.json +++ b/syntaxes/csharp-new.json @@ -61,6 +61,9 @@ }, { "include": "#struct-declaration" + }, + { + "include": "#punctuation-semicolon" } ] }, @@ -144,6 +147,25 @@ } ] }, + "interface-members": { + "patterns": [ + { + "include": "#event-declaration" + }, + { + "include": "#property-declaration" + }, + { + "include": "#indexer-declaration" + }, + { + "include": "#method-declaration" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, "statement": { "patterns": [ { @@ -663,7 +685,7 @@ }, "patterns": [ { - "include": "#punctuation-semicolon" + "include": "#interface-members" } ] } @@ -1098,14 +1120,14 @@ ] }, "method-declaration": { - "begin": "(?=(?(?:\\b(?:new|public|protected|internal|private|static|virtual|sealed|override|abstract|extern|async|partial)\\b\\s*)*)\\s*(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)\\s+(?:(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)(\\s*\\.\\s*))?(?[_$[:alpha:]][_$[:alnum:]]*)(?:\\s*<\\s*\\g(?:\\s*,\\s*\\g)*\\s*>\\s*)?\\s*(?:\\())", + "begin": "(?=(?(?:\\b(?:new|public|protected|internal|private|static|virtual|sealed|override|abstract|extern|async|partial)\\b\\s*)*)\\s*(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)\\s+(?:(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)(\\s*\\.\\s*))?(?(?[_$[:alpha:]][_$[:alnum:]]*)(?:\\s*<\\s*\\g(?:\\s*,\\s*\\g)*\\s*>\\s*)?)\\s*(?:\\())", "end": "(?=\\}|;)", "patterns": [ { "include": "#comment" }, { - "match": "(?(?:\\b(?:new|public|protected|internal|private|static|virtual|sealed|override|abstract|extern|async|partial)\\b\\s*)*)\\s*(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)\\s+(?:(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)(\\s*\\.\\s*))?(?[_$[:alpha:]][_$[:alnum:]]*)(?:\\s*<\\s*\\g(?:\\s*,\\s*\\g)*\\s*>\\s*)?\\s*(?=\\()", + "match": "(?(?:\\b(?:new|public|protected|internal|private|static|virtual|sealed|override|abstract|extern|async|partial)\\b\\s*)*)\\s*(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)\\s+(?:(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\\s*\\:\\:\\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\\s*\\.\\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\\s*<\\s*(?:\\g)(?:\\s*,\\s*\\g)*\\s*>\\s*)?(?:(?:\\*)*)?(?:(?:\\[,*\\])*)?(?:\\s*\\.\\s*\\g)*)|(?:\\s*\\(\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\\s*,\\s*(?:\\g)(?:\\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\\s*\\)\\s*))(?:(?:\\[,*\\])*)?)(\\s*\\.\\s*))?(?(?[_$[:alpha:]][_$[:alnum:]]*)(?:\\s*<\\s*\\g(?:\\s*,\\s*\\g)*\\s*>\\s*)?)\\s*(?=\\()", "captures": { "1": { "patterns": [ @@ -1148,6 +1170,9 @@ { "include": "#parenthesized-parameter-list" }, + { + "include": "#generic-constraints" + }, { "include": "#expression-body" }, diff --git a/syntaxes/syntax.md b/syntaxes/syntax.md index 5cdedc9..94ebc81 100644 --- a/syntaxes/syntax.md +++ b/syntaxes/syntax.md @@ -1,7 +1,6 @@ ## TODO List: * Declarations: - * Interface members * Constructor initializers * Statements/Expressions: @@ -117,13 +116,13 @@ if you consider that regular expressions don't know that "class" is a keyword. T #### Method declarations -* Expression: `(?=(?(?:\b(?:new|public|protected|internal|private|static|virtual|sealed|override|abstract|extern|async|partial)\b\s*)*)\s*(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\s*\:\:\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\s*\.\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\s*<\s*(?:\g)(?:\s*,\s*\g)*\s*>\s*)?(?:(?:\*)*)?(?:(?:\[,*\])*)?(?:\s*\.\s*\g)*)|(?:\s*\(\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\s*,\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\s*\)\s*))(?:(?:\[,*\])*)?)\s+(?:(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\s*\:\:\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\s*\.\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\s*<\s*(?:\g)(?:\s*,\s*\g)*\s*>\s*)?(?:(?:\*)*)?(?:(?:\[,*\])*)?(?:\s*\.\s*\g)*)|(?:\s*\(\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\s*,\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\s*\)\s*))(?:(?:\[,*\])*)?)(\s*\.\s*))?(?[_$[:alpha:]][_$[:alnum:]]*)(?:\s*<\s*\g(?:\s*,\s*\g)*\s*>\s*)?\s*(?:\())` +* Expression: `(?=(?(?:\b(?:new|public|protected|internal|private|static|virtual|sealed|override|abstract|extern|async|partial)\b\s*)*)\s*(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\s*\:\:\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\s*\.\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\s*<\s*(?:\g)(?:\s*,\s*\g)*\s*>\s*)?(?:(?:\*)*)?(?:(?:\[,*\])*)?(?:\s*\.\s*\g)*)|(?:\s*\(\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\s*,\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\s*\)\s*))(?:(?:\[,*\])*)?)\s+(?:(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\s*\:\:\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\s*\.\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\s*<\s*(?:\g)(?:\s*,\s*\g)*\s*>\s*)?(?:(?:\*)*)?(?:(?:\[,*\])*)?(?:\s*\.\s*\g)*)|(?:\s*\(\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\s*,\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\s*\)\s*))(?:(?:\[,*\])*)?)(\s*\.\s*))?(?(?[_$[:alpha:]][_$[:alnum:]]*)(?:\s*<\s*\g(?:\s*,\s*\g)*\s*>\s*)?)\s*(?:\())` * Break down: * Storage modifiers: `(?(?:\b(?:new|public|protected|internal|private|static|virtual|sealed|override|abstract|extern|async|partial)\b\s*)*)` * Type name: `\s*(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\s*\:\:\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\s*\.\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\s*<\s*(?:\g)(?:\s*,\s*\g)*\s*>\s*)?(?:(?:\*)*)?(?:(?:\[,*\])*)?(?:\s*\.\s*\g)*)|(?:\s*\(\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\s*,\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\s*\)\s*))(?:(?:\[,*\])*)?)` * Interface name: `\s+(?:(?(?:(?:[_$[:alpha:]][_$[:alnum:]]*\s*\:\:\s*)?(?:(?:[_$[:alpha:]][_$[:alnum:]]*(?:\s*\.\s*[_$[:alpha:]][_$[:alnum:]]*)*)(?:\s*<\s*(?:\g)(?:\s*,\s*\g)*\s*>\s*)?(?:(?:\*)*)?(?:(?:\[,*\])*)?(?:\s*\.\s*\g)*)|(?:\s*\(\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?(?:\s*,\s*(?:\g)(?:\s+[_$[:alpha:]][_$[:alnum:]]*)?)*\s*\)\s*))(?:(?:\[,*\])*)?)(\s*\.\s*))?` - * Method name and type parameters: `(?[_$[:alpha:]][_$[:alnum:]]*)(?:\s*<\s*\g(?:\s*,\s*\g)*\s*>\s*)?` + * Method name and type parameters: `(?(?[_$[:alpha:]][_$[:alnum:]]*)(?:\s*<\s*\g(?:\s*,\s*\g)*\s*>\s*)?)` * End: `\s*(?:\()` #### Constructor declarations diff --git a/test/syntaxes/events.test.syntax.ts b/test/syntaxes/events.test.syntax.ts index e8962c1..9193889 100644 --- a/test/syntaxes/events.test.syntax.ts +++ b/test/syntaxes/events.test.syntax.ts @@ -125,5 +125,17 @@ public event Type Event Token.Puncuation.Semicolon, Token.Puncuation.CloseBrace]); }); + + it("declaration in interface", () => { + + const input = Input.InInterface(`event EventHandler Event;`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Keywords.Event, + Token.Type("EventHandler"), + Token.Identifiers.EventName("Event"), + Token.Puncuation.Semicolon]); + }); }); }); \ No newline at end of file diff --git a/test/syntaxes/indexers.test.syntax.ts b/test/syntaxes/indexers.test.syntax.ts index 9b13556..f073400 100644 --- a/test/syntaxes/indexers.test.syntax.ts +++ b/test/syntaxes/indexers.test.syntax.ts @@ -60,5 +60,61 @@ public string this[int index] Token.Puncuation.CloseBracket, Token.Puncuation.Semicolon]); }); + + it("declaration in interface", () => { + + const input = Input.InInterface(`string this[int index] { get; set; }`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Keywords.This, + Token.Puncuation.OpenBracket, + Token.Type("int"), + Token.Variables.Parameter("index"), + Token.Puncuation.CloseBracket, + Token.Puncuation.OpenBrace, + Token.Keywords.Get, + Token.Puncuation.Semicolon, + Token.Keywords.Set, + Token.Puncuation.Semicolon, + Token.Puncuation.CloseBrace]); + }); + + it("declaration in interface (read-only)", () => { + + const input = Input.InInterface(`string this[int index] { get; }`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Keywords.This, + Token.Puncuation.OpenBracket, + Token.Type("int"), + Token.Variables.Parameter("index"), + Token.Puncuation.CloseBracket, + Token.Puncuation.OpenBrace, + Token.Keywords.Get, + Token.Puncuation.Semicolon, + Token.Puncuation.CloseBrace]); + }); + + it("declaration in interface (write-only)", () => { + + const input = Input.InInterface(`string this[int index] { set; }`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Keywords.This, + Token.Puncuation.OpenBracket, + Token.Type("int"), + Token.Variables.Parameter("index"), + Token.Puncuation.CloseBracket, + Token.Puncuation.OpenBrace, + Token.Keywords.Set, + Token.Puncuation.Semicolon, + Token.Puncuation.CloseBrace]); + }); }); }); \ No newline at end of file diff --git a/test/syntaxes/methods.test.syntax.ts b/test/syntaxes/methods.test.syntax.ts index 5bac3ae..6d6062d 100644 --- a/test/syntaxes/methods.test.syntax.ts +++ b/test/syntaxes/methods.test.syntax.ts @@ -52,6 +52,26 @@ int Add(int x, int y) Token.Puncuation.CloseBrace]); }); + it("declaration in with generic constraints", () => { + + const input = Input.InClass(`TResult GetString(T arg) where T : TResult { }`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("TResult"), + Token.Identifiers.MethodName("GetString"), + Token.Puncuation.OpenParen, + Token.Type("T"), + Token.Variables.Parameter("arg"), + Token.Puncuation.CloseParen, + Token.Keywords.Where, + Token.Type("T"), + Token.Puncuation.Colon, + Token.Type("TResult"), + Token.Puncuation.OpenBrace, + Token.Puncuation.CloseBrace]); + }); + it("expression body", () => { const input = Input.InClass(`int Add(int x, int y) => x + y;`); @@ -91,5 +111,58 @@ int Add(int x, int y) Token.Puncuation.CloseParen, Token.Puncuation.Semicolon]); }); + + it("declaration in interface", () => { + + const input = Input.InInterface(`string GetString();`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Identifiers.MethodName("GetString"), + Token.Puncuation.OpenParen, + Token.Puncuation.CloseParen, + Token.Puncuation.Semicolon]); + }); + + it("declaration in interface with parameters", () => { + + const input = Input.InInterface(`string GetString(string format, params object[] args);`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Identifiers.MethodName("GetString"), + Token.Puncuation.OpenParen, + Token.Type("string"), + Token.Variables.Parameter("format"), + Token.Puncuation.Comma, + Token.Keywords.Modifiers.Params, + Token.Type("object"), + Token.Puncuation.OpenBracket, + Token.Puncuation.CloseBracket, + Token.Variables.Parameter("args"), + Token.Puncuation.CloseParen, + Token.Puncuation.Semicolon]); + }); + + it("declaration in interface with generic constraints", () => { + + const input = Input.InInterface(`TResult GetString(T arg) where T : TResult;`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("TResult"), + Token.Identifiers.MethodName("GetString"), + Token.Puncuation.OpenParen, + Token.Type("T"), + Token.Variables.Parameter("arg"), + Token.Puncuation.CloseParen, + Token.Keywords.Where, + Token.Type("T"), + Token.Puncuation.Colon, + Token.Type("TResult"), + Token.Puncuation.Semicolon]); + }); }); }); \ No newline at end of file diff --git a/test/syntaxes/properties.test.syntax.ts b/test/syntaxes/properties.test.syntax.ts index 795eafc..7790ac2 100644 --- a/test/syntaxes/properties.test.syntax.ts +++ b/test/syntaxes/properties.test.syntax.ts @@ -254,5 +254,49 @@ private bool prop2 => true;`); Token.Puncuation.Semicolon, Token.Puncuation.CloseBrace]); }); + + it("declaration in interface", () => { + + const input = Input.InInterface(`string Bar { get; set; }`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Identifiers.PropertyName("Bar"), + Token.Puncuation.OpenBrace, + Token.Keywords.Get, + Token.Puncuation.Semicolon, + Token.Keywords.Set, + Token.Puncuation.Semicolon, + Token.Puncuation.CloseBrace]); + }); + + it("declaration in interface (read-only)", () => { + + const input = Input.InInterface(`string Bar { get; }`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Identifiers.PropertyName("Bar"), + Token.Puncuation.OpenBrace, + Token.Keywords.Get, + Token.Puncuation.Semicolon, + Token.Puncuation.CloseBrace]); + }); + + it("declaration in interface (write-only)", () => { + + const input = Input.InInterface(`string Bar { set; }`); + const tokens = tokenize(input); + + tokens.should.deep.equal([ + Token.Type("string"), + Token.Identifiers.PropertyName("Bar"), + Token.Puncuation.OpenBrace, + Token.Keywords.Set, + Token.Puncuation.Semicolon, + Token.Puncuation.CloseBrace]); + }); }); }); \ No newline at end of file diff --git a/test/syntaxes/utils/tokenize.ts b/test/syntaxes/utils/tokenize.ts index e7b76cf..9c11786 100644 --- a/test/syntaxes/utils/tokenize.ts +++ b/test/syntaxes/utils/tokenize.ts @@ -78,6 +78,19 @@ class TestClass { return new Input(lines, { startLine: 2, startIndex: 4, endLine: lines.length - 1, endIndex: 0 }); } + public static InInterface(input: string) { + let text = ` +interface TestInterface { + ${input} +}`; + + // ensure consistent line-endings irrelevant of OS + text = text.replace('\r\n', '\n'); + let lines = text.split('\n'); + + return new Input(lines, { startLine: 2, startIndex: 4, endLine: lines.length - 1, endIndex: 0 }); + } + public static InMethod(input: string) { let text = ` class TestClass {