From b331bec802f9e09ab586d71630867ee8328e26bf Mon Sep 17 00:00:00 2001 From: spiltcoffee Date: Mon, 11 Mar 2019 14:50:42 +1100 Subject: [PATCH 1/2] test: improved coverage --- .../@postdfm/dfm2ast/__test__/broken/form.dfm | 4 + .../dfm2ast/__test__/broken/index.test.ts | 11 + .../{expected.json => everything/ast.json} | 397 ++++++++++-------- .../{sample.dfm => everything/form.dfm} | 19 +- .../__test__/{ => everything}/index.test.ts | 32 +- 5 files changed, 273 insertions(+), 190 deletions(-) create mode 100644 packages/@postdfm/dfm2ast/__test__/broken/form.dfm create mode 100644 packages/@postdfm/dfm2ast/__test__/broken/index.test.ts rename packages/@postdfm/dfm2ast/__test__/{expected.json => everything/ast.json} (50%) rename packages/@postdfm/dfm2ast/__test__/{sample.dfm => everything/form.dfm} (67%) rename packages/@postdfm/dfm2ast/__test__/{ => everything}/index.test.ts (60%) diff --git a/packages/@postdfm/dfm2ast/__test__/broken/form.dfm b/packages/@postdfm/dfm2ast/__test__/broken/form.dfm new file mode 100644 index 00000000..a8c56710 --- /dev/null +++ b/packages/@postdfm/dfm2ast/__test__/broken/form.dfm @@ -0,0 +1,4 @@ +object Form1: TForm1 + Left = 192 + Font.Name = 'MS Sans Serif' + Font.Style = [] diff --git a/packages/@postdfm/dfm2ast/__test__/broken/index.test.ts b/packages/@postdfm/dfm2ast/__test__/broken/index.test.ts new file mode 100644 index 00000000..7355ba85 --- /dev/null +++ b/packages/@postdfm/dfm2ast/__test__/broken/index.test.ts @@ -0,0 +1,11 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as dfm2ast from "../../src"; + +describe("dfm2ast", () => { + test("broken DFM throws error", () => { + const dfm = fs.readFileSync(path.join(__dirname, "form.dfm"), "ascii"); + + expect(() => dfm2ast.parse(dfm)).toThrowError("Unexpected End Of Input"); + }); +}); diff --git a/packages/@postdfm/dfm2ast/__test__/expected.json b/packages/@postdfm/dfm2ast/__test__/everything/ast.json similarity index 50% rename from packages/@postdfm/dfm2ast/__test__/expected.json rename to packages/@postdfm/dfm2ast/__test__/everything/ast.json index 0124c446..6f21221f 100644 --- a/packages/@postdfm/dfm2ast/__test__/expected.json +++ b/packages/@postdfm/dfm2ast/__test__/everything/ast.json @@ -1,173 +1,224 @@ -{ - "astType": "object", - "kind": "object", - "name": "Form1", - "type": "TForm1", - "properties": [ - { - "astType": "property", - "name": "Left", - "value": { - "astType": "integer", - "value": "192" - } - }, - { - "astType": "property", - "name": "Font.Name", - "value": { - "astType": "string", - "value": "MS Sans Serif" - } - }, - { - "astType": "property", - "name": "Font.Style", - "value": { - "astType": "qualifiedList", - "values": [] - } - } - ], - "children": [ - { - "astType": "object", - "children": [], - "kind": "object", - "name": "Image1", - "type": "TImage", - "properties": [ - { - "astType": "property", - "name": "Picture.Data", - "value": { - "astType": "hexString", - "value": "07544269746D617036550000424D365500000000000036000000280000005500000055000000010018000000000000550000C40E0000C40E0000000000000000" - } - } - ] - }, - { - "astType": "object", - "children": [], - "kind": "object", - "name": "ListBox1", - "type": "TListBox", - "properties": [ - { - "astType": "property", - "name": "Items.Strings", - "value": { - "astType": "stringList", - "values": "this is string.\r\nand another one...and another one...now with apostrophe '\r\n" - } - } - ] - }, - { - "astType": "object", - "children": [], - "kind": "object", - "name": "DBGrid1", - "type": "TDBGrid", - "properties": [ - { - "astType": "property", - "name": "Options", - "value": { - "astType": "qualifiedList", - "values": [ - { - "astType": "qualified", - "value": "dgTitles" - }, - { - "astType": "qualified", - "value": "dgIndicator" - }, - { - "astType": "qualified", - "value": "dgColumnResize" - }, - { - "astType": "qualified", - "value": "dgColLines" - }, - { - "astType": "qualified", - "value": "dgRowLines" - }, - { - "astType": "qualified", - "value": "dgTabs" - }, - { - "astType": "qualified", - "value": "dgRowSelect" - }, - { - "astType": "qualified", - "value": "dgConfirmDelete" - }, - { - "astType": "qualified", - "value": "dgCancelOnExit" - } - ] - } - }, - { - "astType": "property", - "name": "Columns", - "value": { - "astType": "itemList", - "values": [ - { - "astType": "item", - "properties": [ - { - "astType": "property", - "name": "Expanded", - "value": { - "astType": "boolean", - "value": false - } - }, - { - "astType": "property", - "name": "Visible", - "value": { - "astType": "boolean", - "value": true - } - } - ] - }, - { - "astType": "item", - "properties": [ - { - "astType": "property", - "name": "Expanded", - "value": { - "astType": "boolean", - "value": false - } - }, - { - "astType": "property", - "name": "Visible", - "value": { - "astType": "boolean", - "value": true - } - } - ] - } - ] - } - } - ] - } - ] -} +{ + "astType": "object", + "kind": "object", + "name": "Form1", + "type": "TForm1", + "properties": [ + { + "astType": "property", + "name": "Left", + "value": { + "astType": "integer", + "value": "192" + } + }, + { + "astType": "property", + "name": "Font.Name", + "value": { + "astType": "string", + "value": "MS Sans Serif" + } + }, + { + "astType": "property", + "name": "Font.Style", + "value": { "astType": "qualifiedList", "values": [] } + } + ], + "children": [ + { + "astType": "object", + "kind": "object", + "name": "Image1", + "type": "TImage", + "properties": [ + { + "astType": "property", + "name": "Picture.Data", + "value": { + "astType": "hexString", + "value": "07544269746D617036550000424D365500000000000036000000280000005500000055000000010018000000000000550000C40E0000C40E0000000000000000" + } + }, + { + "astType": "property", + "name": "Picture.MoreData", + "value": { "astType": "hexString", "value": "" } + } + ], + "children": [] + }, + { + "astType": "object", + "kind": "inherited", + "name": "ListBox1", + "type": "TListBox", + "order": "2", + "properties": [ + { + "astType": "property", + "name": "Items.Strings", + "value": { + "astType": "stringList", + "values": "this is string.\r\nand another one...and another one...now with apostrophe '\r\n" + } + }, + { + "astType": "property", + "name": "Items.MoreStrings", + "value": { "astType": "stringList", "values": [] } + } + ], + "children": [] + }, + { + "astType": "object", + "kind": "object", + "name": "DBGrid1", + "type": "TDBGrid", + "properties": [ + { + "astType": "property", + "name": "Options", + "value": { + "astType": "qualifiedList", + "values": [ + { "astType": "qualified", "value": "dgTitles" }, + { "astType": "qualified", "value": "dgIndicator" }, + { "astType": "qualified", "value": "dgColumnResize" }, + { "astType": "qualified", "value": "dgColLines" }, + { "astType": "qualified", "value": "dgRowLines" }, + { "astType": "qualified", "value": "dgTabs" }, + { "astType": "qualified", "value": "dgRowSelect" }, + { + "astType": "qualified", + "value": "dgConfirmDelete" + }, + { + "astType": "qualified", + "value": "dgCancelOnExit" + } + ] + } + }, + { + "astType": "property", + "name": "MoreOptions", + "value": { + "astType": "qualifiedList", + "values": [] + } + }, + { + "astType": "property", + "name": "Columns", + "value": { + "astType": "itemList", + "values": [ + { + "astType": "item", + "properties": [ + { + "astType": "property", + "name": "Expanded", + "value": { + "astType": "boolean", + "value": false + } + }, + { + "astType": "property", + "name": "Visible", + "value": { "astType": "boolean", "value": true } + } + ] + }, + { + "astType": "item", + "properties": [ + { + "astType": "property", + "name": "Expanded", + "value": { "astType": "boolean", "value": false } + }, + { + "astType": "property", + "name": "Visible", + "value": { "astType": "boolean", "value": true } + } + ] + } + ] + } + }, + { + "astType": "property", + "name": "MoreColumns", + "value": { "astType": "itemList", "values": [] } + } + ], + "children": [] + }, + { + "astType": "object", + "kind": "inline", + "name": "Edit1", + "type": "TEdit", + "properties": [ + { + "astType": "property", + "name": "MaxLength", + "value": { "astType": "double", "value": "6e20" } + }, + { + "astType": "property", + "name": "MinLength", + "value": { "astType": "double", "value": "-20e1" } + }, + { + "astType": "property", + "name": "SomeOtherLength", + "value": { "astType": "double", "value": "45.333e20" } + }, + { + "astType": "property", + "name": "Color", + "value": { "astType": "integer", "value": "FF0000" } + } + ], + "children": [] + }, + { + "astType": "object", + "kind": "object", + "name": "SubForm", + "type": "TForm2", + "properties": [], + "children": [ + { + "astType": "object", + "kind": "inline", + "name": "SubEdit", + "type": "TEdit", + "properties": [ + { + "astType": "property", + "name": "Value", + "value": { "astType": "string", "value": "" } + } + ], + "children": [] + } + ] + }, + { + "astType": "object", + "kind": "object", + "name": "Nothing", + "type": "TNothing", + "properties": [], + "children": [] + } + ] +} diff --git a/packages/@postdfm/dfm2ast/__test__/sample.dfm b/packages/@postdfm/dfm2ast/__test__/everything/form.dfm similarity index 67% rename from packages/@postdfm/dfm2ast/__test__/sample.dfm rename to packages/@postdfm/dfm2ast/__test__/everything/form.dfm index 9362802e..219a4d9b 100644 --- a/packages/@postdfm/dfm2ast/__test__/sample.dfm +++ b/packages/@postdfm/dfm2ast/__test__/everything/form.dfm @@ -7,17 +7,20 @@ object Form1: TForm1 07544269746D617036550000424D365500000000000036000000280000005500 000055000000010018000000000000550000C40E0000C40E0000000000000000 } + Picture.MoreData = {} end - object ListBox1: TListBox + inherited ListBox1: TListBox [2] Items.Strings = ( 'this is string.' 'and another one...' + 'and another one...' + 'now with apostrophe '''#13#10 ) + Items.MoreStrings = () end object DBGrid1: TDBGrid Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit] + MoreOptions = [] Columns = < item Expanded = False @@ -27,5 +30,19 @@ object Form1: TForm1 Expanded = False Visible = True end> + MoreColumns = <> + end + inline Edit1: TEdit + MaxLength = 6e20 + MinLength = -20e1 + SomeOtherLength = 45.333e20 + Color = $FF0000 + end + object SubForm: TForm2 + inline SubEdit: TEdit + Value = '' + end + end + object Nothing: TNothing end end diff --git a/packages/@postdfm/dfm2ast/__test__/index.test.ts b/packages/@postdfm/dfm2ast/__test__/everything/index.test.ts similarity index 60% rename from packages/@postdfm/dfm2ast/__test__/index.test.ts rename to packages/@postdfm/dfm2ast/__test__/everything/index.test.ts index 16f3e29e..879212be 100644 --- a/packages/@postdfm/dfm2ast/__test__/index.test.ts +++ b/packages/@postdfm/dfm2ast/__test__/everything/index.test.ts @@ -1,16 +1,16 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as dfm2ast from "../src"; - -describe("dfm2ast", () => { - test("converted AST matches expected AST", () => { - const dfm = fs.readFileSync(path.join(__dirname, "sample.dfm"), "ascii"); - - const received = JSON.parse(JSON.stringify(dfm2ast.parse(dfm))); - const expected = JSON.parse( - fs.readFileSync(path.join(__dirname, "expected.json"), "utf8") - ); - - expect(received).toEqual(expected); - }); -}); +import * as fs from "fs"; +import * as path from "path"; +import * as dfm2ast from "../../src"; + +describe("dfm2ast", () => { + test("converted AST matches expected AST", () => { + const dfm = fs.readFileSync(path.join(__dirname, "form.dfm"), "ascii"); + + const received = JSON.parse(JSON.stringify(dfm2ast.parse(dfm))); + const expected = JSON.parse( + fs.readFileSync(path.join(__dirname, "ast.json"), "utf8") + ); + + expect(received).toEqual(expected); + }); +}); From 95d69b07fc8e3991d8f08867ba1400671879b8f2 Mon Sep 17 00:00:00 2001 From: spiltcoffee Date: Mon, 11 Mar 2019 16:10:30 +1100 Subject: [PATCH 2/2] fix(@postdfm/dfm2ast): emptry string ('') marked as unknown syntax --- .../dfm2ast/__test__/everything/ast.json | 2 +- .../dfm2ast/__test__/everything/form.dfm | 2 +- packages/@postdfm/dfm2ast/ne/grammar.ne | 37 ++++++++++++------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/@postdfm/dfm2ast/__test__/everything/ast.json b/packages/@postdfm/dfm2ast/__test__/everything/ast.json index 6f21221f..812a00d0 100644 --- a/packages/@postdfm/dfm2ast/__test__/everything/ast.json +++ b/packages/@postdfm/dfm2ast/__test__/everything/ast.json @@ -61,7 +61,7 @@ "name": "Items.Strings", "value": { "astType": "stringList", - "values": "this is string.\r\nand another one...and another one...now with apostrophe '\r\n" + "values": "this is string.\r\nand another one...and another one...now with apostrophe '\r\nhow fancy" } }, { diff --git a/packages/@postdfm/dfm2ast/__test__/everything/form.dfm b/packages/@postdfm/dfm2ast/__test__/everything/form.dfm index 219a4d9b..1bde4db2 100644 --- a/packages/@postdfm/dfm2ast/__test__/everything/form.dfm +++ b/packages/@postdfm/dfm2ast/__test__/everything/form.dfm @@ -14,7 +14,7 @@ object Form1: TForm1 ( 'this is string.' 'and another one...' + 'and another one...' - + 'now with apostrophe '''#13#10 + + 'now with apostrophe '''#13#10'how fancy' ) Items.MoreStrings = () end diff --git a/packages/@postdfm/dfm2ast/ne/grammar.ne b/packages/@postdfm/dfm2ast/ne/grammar.ne index 00a800df..dc689b44 100644 --- a/packages/@postdfm/dfm2ast/ne/grammar.ne +++ b/packages/@postdfm/dfm2ast/ne/grammar.ne @@ -34,7 +34,7 @@ property -> qualifiedName _ "=" _ value {% d => new AST.Property(d[0], d[4]) %} value -> boolean {% d => new AST.BooleanValue(d[0].toLowerCase() === "true") %} | integer {% d => new AST.IntegerValue(d[0]) %} | double {% d => new AST.DoubleValue(d[0]) %} - | string {% d => new AST.StringValue(d[0]) %} + | string {% d => new AST.StringValue(d[0].value) %} | qualifiedName {% (d, _, reject) => { if (keywords.includes(d[0].toLowerCase())) { return reject; @@ -54,9 +54,11 @@ value -> boolean {% d => new AST.BooleanValue(d[0].toLowerCase() commaValues -> value {% d => [d[0]] %} | commaValues _ "," _ value {% d => [].concat(d[0], d[4]) %} -plusValues -> string {% d => d[0] %} - | plusValues _ "+" _ string {% d => d[0] + d[4] %} # plus implies string on sameline - | plusValues __ string {% d => d[0] + "\r\n" + d[2] %} # space implies string on newline +plusValues -> string {% d => d[0].value %} + # plus implies string on sameline + | plusValues _ "+" _ string {% d => d[0] + d[4].value %} + # space implies string on newline + | plusValues __ string {% d => d[0] + "\r\n" + d[2].value %} hexValues -> hexString {% d => d[0] %} | hexValues __ hexString {% d => d[0] + d[2] %} @@ -69,24 +71,32 @@ name -> letter {% id %} | name alphanumeric {% d => d[0] + d[1] %} typeName -> name {% d => ({ type: d[0] }) %} - | name _ "[" _ natural _ "]" {% d => ({ type: d[0], order: d[4] }) %} # what is the natural for? + | name _ "[" _ natural _ "]" {% d => ({ type: d[0], order: d[4] }) %} qualifiedName -> name {% id %} | name "." name {% d => d[0] + "." + d[2] %} string -> singleString {% id %} - | string singleString {% d => d[0] + d[1] %} + # two literals next to each other cause an apostrophe to appear + | string singleString {% d => { + const areBothLiterals = d[0].type === "literal" && d[1].type === "literal"; -singleString -> literalString {% id %} - | controlChar {% id %} + return { + type: d[1].type, + value: + d[0].value + + (areBothLiterals ? "'" : "") + + d[1].value + } + } %} -literalString -> "'" quotedString "'" {% d => d[1] %} +singleString -> controlChar {% d => ({ type: "control", value: d[0] }) %} + | literalString {% d => ({ type: "literal", value: d[0] }) %} -quotedString -> quotedStringChar {% id %} - | quotedString quotedStringChar {% d => d[0] + d[1] %} +literalString -> "'" quotedString "'" {% d => d[1] %} -quotedStringChar -> "''" {% () => "'" %} # escape single quote - | [^'] {% id %} +quotedString -> null {% () => "" %} + | quotedString [^'] {% d => d[0] + d[1] %} controlChar -> "#" natural {% d => String.fromCharCode(d[1]) %} @@ -111,7 +121,6 @@ hexString -> hexDigit {% id %} integer -> int {% id %} | hex {% id %} -#todo: improve? double -> int "." natural {% d => `${d[0]}.${d[2]}` %} | int "e"i int {% d => `${d[0]}e${d[2]}` %} | int "." natural "e"i int {% d => `${d[0]}.${d[2]}e${d[4]}` %}