From 285ee839d23cf1ff0cd5bf23381b79c202cf5f36 Mon Sep 17 00:00:00 2001 From: Jean Privat Date: Thu, 13 Apr 2023 20:03:05 -0400 Subject: [PATCH 1/2] extract TonelWriter>>#writeMethodBody:on: --- .../TonelWriter.class/instance/writeMethodBody.on..st | 6 ++++++ .../instance/writeMethodDefinition.parent.on..st | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st diff --git a/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st b/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st new file mode 100644 index 0000000..da120da --- /dev/null +++ b/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st @@ -0,0 +1,6 @@ +private - writing +writeMethodBody: methodBody on: aStream + + | nl | + nl := self newLine. + aStream << ' [' << methodBody << nl << ']' << nl \ No newline at end of file diff --git a/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodDefinition.parent.on..st b/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodDefinition.parent.on..st index c2b5742..efc8249 100644 --- a/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodDefinition.parent.on..st +++ b/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodDefinition.parent.on..st @@ -10,5 +10,5 @@ writeMethodDefinition: aMethodDefinition parent: aClassDefinition on: aStream aStream << nl << (self methodDefinitionOf: aMethodDefinition) << nl - << fullClassName << ' >> ' << methodDeclaration - << ' [' << methodBody << nl << ']' << nl ] \ No newline at end of file + << fullClassName << ' >> ' << methodDeclaration. + self writeMethodBody: methodBody on: aStream ] \ No newline at end of file From af86aa4f34a264c90cb6f962aa8209e21812c202 Mon Sep 17 00:00:00 2001 From: Jean Privat Date: Thu, 13 Apr 2023 21:42:41 -0400 Subject: [PATCH 2/2] Propose a fallback robust chunk format --- .../TonelParser.class/instance/method.st | 2 +- .../TonelParser.class/instance/selector.st | 9 +++ .../TonelSourceScanner.class/instance/scan.st | 12 ++++ .../instance/writeMethodBody.on..st | 15 ++++- .../instance/testMethodRobustBody.st | 18 ++++++ .../testWriteRobustMethodDefinitionOn.st | 63 +++++++++++++++++++ 6 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 MonticelloTonel-Core.package/TonelParser.class/instance/selector.st create mode 100644 MonticelloTonel-Tests.package/TonelParserTest.class/instance/testMethodRobustBody.st create mode 100644 MonticelloTonel-Tests.package/TonelWriterTest.class/instance/testWriteRobustMethodDefinitionOn.st diff --git a/MonticelloTonel-Core.package/TonelParser.class/instance/method.st b/MonticelloTonel-Core.package/TonelParser.class/instance/method.st index 382b479..ab8c374 100644 --- a/MonticelloTonel-Core.package/TonelParser.class/instance/method.st +++ b/MonticelloTonel-Core.package/TonelParser.class/instance/method.st @@ -3,7 +3,7 @@ method | type selector | type := self untilIncluding: '>>'. - selector := self cleanSelector: (self untilExcluding: '['). + selector := self cleanSelector: (self selector). type := type trimBoth substrings: ' '. type size = 1 ifTrue: [ type := type copyWith: nil ]. ^ { diff --git a/MonticelloTonel-Core.package/TonelParser.class/instance/selector.st b/MonticelloTonel-Core.package/TonelParser.class/instance/selector.st new file mode 100644 index 0000000..e34fc61 --- /dev/null +++ b/MonticelloTonel-Core.package/TonelParser.class/instance/selector.st @@ -0,0 +1,9 @@ +parsing +selector + + ^ String streamContents: [ :out | + [ stream atEnd or: [ stream peek isSeparator not ] ] whileFalse: [ stream next ]. + [ stream atEnd or: [ stream peek isSeparator ] ] whileFalse: [ + out nextPut: stream next ]. + [ stream atEnd or: [ '[!' includes: stream peek ] ] whileFalse: [ + out nextPut: stream next ] ] \ No newline at end of file diff --git a/MonticelloTonel-Core.package/TonelSourceScanner.class/instance/scan.st b/MonticelloTonel-Core.package/TonelSourceScanner.class/instance/scan.st index 260c39d..22e513c 100644 --- a/MonticelloTonel-Core.package/TonelSourceScanner.class/instance/scan.st +++ b/MonticelloTonel-Core.package/TonelSourceScanner.class/instance/scan.st @@ -1,6 +1,18 @@ scanning scan self prepareToScan. + stream peek = $! ifTrue: [ + "Robust mode, source use a *chunk* format. existing ! are escaped (doubled)" + stream next. + ^ (String streamContents: [ :str | + | ch | + [ ch := stream peek. + ch ~= $! or: [ + stream next. + ch := stream peek. + ch = $! ] ] + whileTrue: [ str nextPut: stream next ] ] ) withInternalLineEndings ]. + stream peek = $[ ifFalse: [ TonelParseError signal: 'Can''t parse method body' ]. [ stream atEnd or: [ isFinished ] ] whileFalse: [ self scanNextChunk ]. diff --git a/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st b/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st index da120da..9312d70 100644 --- a/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st +++ b/MonticelloTonel-Core.package/TonelWriter.class/instance/writeMethodBody.on..st @@ -1,6 +1,19 @@ private - writing writeMethodBody: methodBody on: aStream - | nl | + | nl reader | nl := self newLine. + + "Check is the current body is robust. The most correct approach is to try to read it back" + reader := ('[' , methodBody , ']') readStream. + [ (TonelSourceScanner on: reader) scan ] on: TonelParseError do: [ reader := nil ]. + (reader isNotNil and: [ reader atEnd ]) ifFalse: [ + "If parse failed, then use an alternative robust *chunk* format instead. + Body is strictly delimited by `!` and existing `!` in the content are doubled" + aStream << ' !'. + methodBody do: [ :each | + each = $! ifTrue: [ aStream << $! ]. + aStream << each ]. + aStream << '!' << nl. + ^ self ]. aStream << ' [' << methodBody << nl << ']' << nl \ No newline at end of file diff --git a/MonticelloTonel-Tests.package/TonelParserTest.class/instance/testMethodRobustBody.st b/MonticelloTonel-Tests.package/TonelParserTest.class/instance/testMethodRobustBody.st new file mode 100644 index 0000000..59ee225 --- /dev/null +++ b/MonticelloTonel-Tests.package/TonelParserTest.class/instance/testMethodRobustBody.st @@ -0,0 +1,18 @@ +tests +testMethodRobustBody + self + assertParse: '!¿["''#(#{({!' + rule: #methodBody + equals: '¿["''#(#{({'. + self + assertParse: '!])}¿!' + rule: #methodBody + equals: '])}¿'. + self + assertParse: '!!!!' + rule: #methodBody + equals: '!'. + self + assertParse: '!!!a!!!!b!!!' + rule: #methodBody + equals: '!a!!b!'. \ No newline at end of file diff --git a/MonticelloTonel-Tests.package/TonelWriterTest.class/instance/testWriteRobustMethodDefinitionOn.st b/MonticelloTonel-Tests.package/TonelWriterTest.class/instance/testWriteRobustMethodDefinitionOn.st new file mode 100644 index 0000000..9a15336 --- /dev/null +++ b/MonticelloTonel-Tests.package/TonelWriterTest.class/instance/testWriteRobustMethodDefinitionOn.st @@ -0,0 +1,63 @@ +tests +testWriteRobustMethodDefinitionOn + + | writer def stream | + writer := self actualClass new. + + "To much closer" + stream := String new writeStream. + def := MCMethodDefinition + className: #Object + classIsMeta: false + selector: #selector + category: 'accessing' + timeStamp: nil + source: 'selector ^ ] 42'. + writer writeMethodDefinition: def on: stream. + self + assert: stream contents lines last + equals: 'Object >> selector ! ^ ] 42!'. + + "To much opener" + stream := String new writeStream. + def := MCMethodDefinition + className: #Object + classIsMeta: false + selector: #selector + category: 'accessing' + timeStamp: nil + source: 'selector ^ [ 42'. + writer writeMethodDefinition: def on: stream. + self + assert: stream contents lines last + equals: 'Object >> selector ! ^ [ 42!'. + + "To much quotes" + stream := String new writeStream. + def := MCMethodDefinition + className: #Object + classIsMeta: false + selector: #selector + category: 'accessing' + timeStamp: nil + source: 'selector ^ " 42'. + + writer writeMethodDefinition: def on: stream. + self + assert: stream contents lines last + equals: 'Object >> selector ! ^ " 42!'. + + "To much everything" + stream := String new writeStream. + def := MCMethodDefinition + className: #Object + classIsMeta: false + selector: #selector + category: 'accessing' + timeStamp: nil + source: 'selector ^ " '' #[ [ #( { ! !! 42 " '' ] ) }'. + + writer writeMethodDefinition: def on: stream. + self + assert: stream contents lines last + equals: 'Object >> selector ! ^ " '' #[ [ #( { !! !!!! 42 " '' ] ) }!'. \ No newline at end of file