From 5081ebbd30a8811bea4c2df4bef684c426adbaa6 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Wed, 31 Aug 2011 09:46:25 -0500 Subject: [PATCH] Basic transformative functionality implemented. * Updated test data to include additional parsing edge cases. * Updated `vbs_db_records.hrl` to use `@org` directives. * Refactored Generator/Emitter dual-object phase concept into one object, the Generator. The emitter ended up needing basically full visibility into the generator anyways. * Implemented `JLPBaseGenerator`, `MarkdownGenerator`, and `TransparentGenerator` * Modified the way the parser handles remaining lines to allow it to safely handle empty lines. --- resources/main/test.groovy | 29 ++-- resources/main/vbs_db_records.hrl | 98 ++++++++++--- src/main/com/jdblabs/jlp/EchoEmitter.groovy | 13 -- src/main/com/jdblabs/jlp/Formatter.groovy | 7 - .../com/jdblabs/jlp/FormattingEmitter.groovy | 46 ------ ...Emitter.groovy => JLPBaseGenerator.groovy} | 33 +++-- src/main/com/jdblabs/jlp/JLPPegParser.java | 38 +++-- .../jdblabs/jlp/JLPPegParser.java.noactions | 133 ------------------ .../com/jdblabs/jlp/MarkdownEmitter.groovy | 23 --- .../com/jdblabs/jlp/MarkdownFormatter.groovy | 16 --- .../com/jdblabs/jlp/MarkdownGenerator.groovy | 64 +++++++++ src/main/com/jdblabs/jlp/ParserActions.groovy | 7 - .../jdblabs/jlp/TransparentFormatter.groovy | 7 - .../jdblabs/jlp/TransparentGenerator.groovy | 24 ++++ 14 files changed, 231 insertions(+), 307 deletions(-) delete mode 100644 src/main/com/jdblabs/jlp/EchoEmitter.groovy delete mode 100644 src/main/com/jdblabs/jlp/Formatter.groovy delete mode 100644 src/main/com/jdblabs/jlp/FormattingEmitter.groovy rename src/main/com/jdblabs/jlp/{JLPBaseEmitter.groovy => JLPBaseGenerator.groovy} (66%) delete mode 100644 src/main/com/jdblabs/jlp/JLPPegParser.java.noactions delete mode 100644 src/main/com/jdblabs/jlp/MarkdownEmitter.groovy delete mode 100644 src/main/com/jdblabs/jlp/MarkdownFormatter.groovy create mode 100644 src/main/com/jdblabs/jlp/MarkdownGenerator.groovy delete mode 100644 src/main/com/jdblabs/jlp/ParserActions.groovy delete mode 100644 src/main/com/jdblabs/jlp/TransparentFormatter.groovy create mode 100644 src/main/com/jdblabs/jlp/TransparentGenerator.groovy diff --git a/resources/main/test.groovy b/resources/main/test.groovy index aa6b0ac..6838edb 100644 --- a/resources/main/test.groovy +++ b/resources/main/test.groovy @@ -1,5 +1,4 @@ -import com.jdblabs.jlp.EchoEmitter -import com.jdblabs.jlp.JLPPegParser +import com.jdblabs.jlp.* import org.parboiled.Parboiled import org.parboiled.parserunners.ReportingParseRunner import org.parboiled.parserunners.RecoveringParseRunner @@ -16,24 +15,32 @@ simpleTest = { %% Second Line %% Third Line Fourth line - %% Fifth line - %% @author Sixth Line - %% @Example Seventh Line - %% Markdown lines (eigth line) - %% Still markdown (ninth line) - Tenth line is a code line + + %% Sixth line + %% @author Seventh Line + %% @Example Eigth Line + %% Markdown lines (ninth line) + %% Still markdown (tenth line) + Eleventh line is a code line """ parseRunner.run(testLine) } vbsTest = { - "Parsing vbs_db_records.hrl into 'vbsResult'." - "--------------------------------------------\n" + println "Parsing vbs_db_records.hrl into 'vbsResult'." + println "--------------------------------------------" vbsTestFile = new File('vbs_db_records.hrl') println "vbsTestFile is ${vbsTestFile.exists() ? 'present' : 'absent'}." vbsTestInput = vbsTestFile.text - parseRunner.run(vbsTestInput) + vbsParsed = parseRunner.run(vbsTestInput) + + vbsResult = MarkdownGenerator.generateDocuments([vbs: vbsParsed.resultValue]).vbs + + println "Writing to file 'vbs_result.html'." + println "----------------------------------" + + (new File('vbs_result.html')).withWriter { out -> out.println vbsResult } } diff --git a/resources/main/vbs_db_records.hrl b/resources/main/vbs_db_records.hrl index 60e0180..58fe73a 100644 --- a/resources/main/vbs_db_records.hrl +++ b/resources/main/vbs_db_records.hrl @@ -1,5 +1,6 @@ % vbs_db_records.erl %% @author Jonathan Bernard +%% #copyright 2010-2011 JDB Labs Inc. %% @doc %% The VBS database API is centered around the data records: @@ -11,25 +12,30 @@ %% the standard VBS database API functions that work with ``vbs_adult`` %% records. %% -%% @section Record Definitions +%% #Record Definitions %% Here are the record definitions: %% @doc Information about an adult in the VBS system. +%% @org records/vbs_adult -record(vbs_adult, { %% @doc A unique number. This is the record's primary identification and %% the table's primary key + %% @org records/vbs_adult/id id, %% @doc A unique full name. %% @example "John Smith", "Fae Alice McDonald" + %% @org records/vbs_adult/name name, %% @doc The adult's age (optional). + %% @org records/vbs_adult/age age = 0, %% @doc A list of phone numbers (strings). %% @example ["512-555-1155", "123-456-7890"] + %% @org records/vbs_adult/phone_numbers phone_numbers, %% @doc The adult's address (optional). There is not pre-defined format, @@ -39,47 +45,56 @@ %% %% "123 Grant Drive %% Plainsville, TX, 78707" + %% @org records/vbs_adult/address address = "", %% @doc The adult's email address as a string. %% @example "john_smith@mailco.com" + %% @org records/vbs_adult/email email = ""}). %% @doc An entry recording a person's attendance. +%% @org records/vbs_attendance -record(vbs_attendance, { %% @doc A unique number. This is the record's primary identification and the %% table's primary key. + %% @org records/vbs_attendance/id id, %% @doc The id of person who attended. This is a foreign key onto either the - %% [`vbs_worker`](doc://records/vbs_worker) or - %% [`vbs_child`](doc://records/vbs_child) table, depending on the value of - %% the [`person_type`](doc://records/vbs_attendance/person_type) field. + %% [`vbs_worker`](jlp://records/vbs_worker) or + %% [`vbs_child`](jlp://records/vbs_child) table, depending on the value of + %% the [`person_type`](jlp://records/vbs_attendance/person_type) field. + %% @org records/vbs_attendance/person_id person_id, %% @doc The type of person who attended. This determines which table the - %% [`person_id`](doc://records/vbs_attendance/person_id) links on. The + %% [`person_id`](jlp://records/vbs_attendance/person_id) links on. The %% possible values and the corresponding link tables are: %% - %% ======== ======================================== - %% `child` [`vbs_child`](doc://records/vbs_child) - %% `worker` [`vbs_worker`](doc://records/vbs_worker) - %% ======== ======================================== + %% Value | Link Table + %% --------|---------------------------------------- + %% `child` |[`vbs_child`](jlp://records/vbs_child) + %% `worker`|[`vbs_worker`](jlp://records/vbs_worker) %% + %% @org records/vbs_attendance/person_type person_type, %% @doc The date of attendance, stored as {Year, Month, Day}. %% @example {2011, 6, 14} + %% @org records/vbs_attendance/date date = {1900, 1, 1}, %% @doc A timestamp taken when the person was signed in, stored as %% {Hour, Minute, Second} %% @example {5, 22, 13} + %% @org records/vbs_attendance/sign_in sign_in = false, % {hour, minute, second} %% @doc A timestamp taken when the person is signed out, stored as %% {Hour, Minute, Second} + %% @org records/vbs_attendance/sign_out sign_out = false, % {hour, minute, second} %% @doc A list of {Key, Value} pairs that can be used to store additional @@ -87,152 +102,199 @@ %% or client-specific data, without having to alter the database schema. %% When working with `vbs_attendance` records, a caller should ignore %% `ext_data` values it does not understand + %% @org records/vbs_attendance/ext_data ext_data = [], %% @doc Any comments for the day about this person. + %% @org records/vbs_attendance/comments comments = ""}). %% @doc Information about a child in the VBS program. +%% @org records/vbs_child -record(vbs_child, { %% @doc A unique number. This is the record's primary identification and the %% table's primary key. + %% @org records/vbs_child/id id, %% @doc The id of the crew to which this child has been assigned. This is a - %% foreign key linking to a [`vbs_crew.id`](doc://records/vbs_crew/id). + %% foreign key linking to a [`vbs_crew.id`](jlp://records/vbs_crew/id). + %% @org records/vbs_child/crew_id crew_id, %% @doc The child's full name. %% @example "Mary Scott", "Gregory Brown" + %% @org records/vbs_child/name name, %% @doc The child's date of birth, stored as {Year, Month, Day} %% @example {1998, 12, 22} + %% @org records/vbs_child/date_of_birth date_of_birth, %% @doc The child's gender, either `male` or `female` + %% @org records/vbs_child/gender gender, %% @doc The child's grade level in school. + %% @org records/vbs_child/grade grade, %% @doc A list of ids representing the child's legal guardians. These link %% the child record to adult records by the - %% [`vbs_adult.id`](doc://records/vbs_adult/id) + %% [`vbs_adult.id`](jlp://records/vbs_adult/id) %% @example [4, 5] + %% @org records/vbs_child/guardian_ids guardian_ids, %% @doc A list of ids, similar to `guardian_ids`, but representing the %% adults that are allowed to pick the children up. These link the child %% record to adult records by - %% ['vbs_adult.id`](doc://records/vbs_adult/id). + %% ['vbs_adult.id`](jlp://records/vbs_adult/id). + %% @org records/vbs_child/pickup_ids pickup_ids, %% @doc A list of ids, similar to `guardian_ids` and `pickup_ids`, but %% representing adults that should be contacted if there is an emergency %% involving this child (injury, for example). These link the child record - %% to adult records by [`vbs_adult.id`](doc://records/vbs_adult/id). + %% to adult records by [`vbs_adult.id`](jlp://records/vbs_adult/id). + %% @org records/vbs_child/emerency_ids emerency_ids, %% @doc The child's home church, usually used if they are not a member of %% the hosting church. + %% @org records/vbs_child/home_church home_church, %% @doc If this child is a visitor, this is used to track who invited them, %% or who brought them. + %% @org records/vbs_child/visitor_of visitor_of, %% @doc Answers the question: Is this child a visitor? Valid values are %% `true` and `false`. + %% @org records/vbs_child/is_visitor is_visitor, %% @doc The date the child registered, stored as {Year, Month, Day} + %% @org records/vbs_child/registration_date registration_date, %% @doc The child's shirt size, stored as a string. + %% @org records/vbs_child/shirt_size shirt_size, %% @doc Any special needs this child has that should be accomodated. + %% @org records/vbs_child/special_needs special_needs, %% @doc Any known allergies this child has. + %% @org records/vbs_child/allergies allergies, %% @doc Additional comments about this child. + %% @org records/vbs_child/comments comments}). %% @doc Information about a crew in the VBS system. +%% @org records/vbs_crew -record(vbs_crew, { %% @doc A unique number. This is the record's primary identification and the %% table's primary key. + %% @org records/vbs_crew/id id, % primary key %% @doc The crew number. + %% @org records/vbs_crew/number number, %% @doc The crew type. This is a foreign key on - %% [`vbs_crew_type.id`](doc://records/vbs_crew_type/id). + %% [`vbs_crew_type.id`](jlp://records/vbs_crew_type/id). + %% @org records/vbs_crew/crew_type_id crew_type_id, % foreign key onto crew_type %% @doc The name of the crew, stored as a string. + %% @org records/vbs_crew/name name, %% @doc Any comments about the crew. + %% @org records/vbs_crew/comments comments = ""}). %% @doc Information about a crew type. Crew types are often used when a VBS %% program has seperate activities set up for different types of children %% (usually based on age). For example, having two type: Elementary and Pre-K %% is common when there is a seperate set of activities for smaller children. +%% @org records/vbs_crew_type -record(vbs_crew_type, { %% @doc A unique number. This is the record's primary identification and the %% table's primary key. + %% @org records/vbs_crew_type/id id, %% @doc The displayed name of the crew type. + %% @org records/vbs_crew_type/name name}). %% @doc The id counter records are used to keep track of the next valid id for a %% specific purpose. This is how the unique id fields in other records is %% implmented. +%% @org records/vbs_id_counter -record(vbs_id_counter, { %% @doc A name for the counter. This is the primary key for the table and %% must be unique. %% @example `vbs_adult_id` - name, % primary key + %% @org records/vbs_id_counter/name + name, %% @doc The next value for this counter. + %% @org records/vbs_id_counter/next_value next_value = 0}). %% @doc Information about workers involved in the VBS program. +%% @org records/vbs_worker -record(vbs_worker, { %% @doc A unique number. This is the record's primary identification and the %% table's primary key. + %% @org records/vbs_worker/id id, - %% @doc Links this worker record to a [`vbs_adult`](doc://records/vbs_adult) + %% @doc Links this worker record to a [`vbs_adult`](jlp://records/vbs_adult) + %% @org records/vbs_worker/adult_id adult_id, % foreign key on adult %% @doc The crew this worker is assigned to. This is a link to - %% [`vbs_crew.id`](doc://records/vbs_crew/id). The most common way to deal + %% [`vbs_crew.id`](jlp://records/vbs_crew/id). The most common way to deal %% with workers who are not assigned to a particular crew is to create a %% special administrative crew and assign all these workers to that crew. + %% @org records/vbs_worker/crew_id crew_id = 0, %% @doc + %% @org records/vbs_worker/worker_type_id worker_type_id, % foreign key on worker_type %% @doc + %% @org records/vbs_worker/shirt_size shirt_size, %% @doc + %% @org records/vbs_worker/ext_data ext_data = []}). +%% @doc +%% @org records/vbs_worker_type -record(vbs_worker_type, { - id, % primary key + + %% @doc + %% @org records/vbs_worker_type/id + id, + + %% @doc + %% @org records/vbs_worker_type/name name}). diff --git a/src/main/com/jdblabs/jlp/EchoEmitter.groovy b/src/main/com/jdblabs/jlp/EchoEmitter.groovy deleted file mode 100644 index 0a88a9d..0000000 --- a/src/main/com/jdblabs/jlp/EchoEmitter.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package com.jdblabs.jlp - -import com.jdblabs.jlp.ast.* - -public class EchoEmitter extends JLPEmitter { - - public String emitAuthor(String value) { "Author: $value" } - public String emitDoc(String value) { value } - public String emitExample(String value) { "Example:\n$value" } - public String emitOrg(String value) { "Org: $value" } - - public String emitBlock(TextBlock textBlock) { textBlock.value } -} diff --git a/src/main/com/jdblabs/jlp/Formatter.groovy b/src/main/com/jdblabs/jlp/Formatter.groovy deleted file mode 100644 index 801d0cf..0000000 --- a/src/main/com/jdblabs/jlp/Formatter.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package com.jdblabs.jlp - -public interface Formatter { - String formatText(String text) - String formatCode(String code) - String formatReference(String ref) } - diff --git a/src/main/com/jdblabs/jlp/FormattingEmitter.groovy b/src/main/com/jdblabs/jlp/FormattingEmitter.groovy deleted file mode 100644 index 691fdfd..0000000 --- a/src/main/com/jdblabs/jlp/FormattingEmitter.groovy +++ /dev/null @@ -1,46 +0,0 @@ -package com.jdblabs.jlp - -import com.jdblabs.jlp.ast.* -import com.jdblabs.jlp.ast.Directive.DirectiveType - -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -public class FormattingEmitter extends JLPBaseEmitter { - - Formatter formatter - private Logger log = LoggerFactory.getLogger(this.getClass()) - - public FormattingEmitter(Formatter f, def generationState) { - super(generationState) - this.formatter = f } - - protected String emit(TextBlock textBlock) { - return formatter.format(textBlock) } - - protected String emit(Directive directive) { - switch (directive.type) { - case DirectiveType.Author: - case DirectiveType.Doc: - case DirectiveType.Example: - case DirectiveType.Org: - def orgValue = directive.value - if generationState.orgs.contains(orgValue) { - log.warn("Duplicate @org id: '${orgValue}'.") - def orgMatcher = (orgValue =~ /(.*)-(\d+)/ - if (orgMatcher.matches()) { - orgValue = "${m[0][1]}-${(m[0][2] as int) + 1}" } - else { orgValue += "-1" } } - - generationState.orgs << orgValue - formatter.formatReference(orgValue) - break } } - - private formatText(String s) { - // fix links to internal targets - s = s.eachMatch(/jlp:\/\/([^\s]+)/, s) - - // format with formatter - return formatter.formatText(s) - } -} diff --git a/src/main/com/jdblabs/jlp/JLPBaseEmitter.groovy b/src/main/com/jdblabs/jlp/JLPBaseGenerator.groovy similarity index 66% rename from src/main/com/jdblabs/jlp/JLPBaseEmitter.groovy rename to src/main/com/jdblabs/jlp/JLPBaseGenerator.groovy index c5ca0ff..131143a 100644 --- a/src/main/com/jdblabs/jlp/JLPBaseEmitter.groovy +++ b/src/main/com/jdblabs/jlp/JLPBaseGenerator.groovy @@ -2,18 +2,28 @@ package com.jdblabs.jlp import com.jdblabs.jlp.ast.* import com.jdblabs.jlp.ast.Directive.DirectiveType +import java.util.List +import java.util.Map -public abstract class JLPBaseEmitter { +public abstract class JLPBaseGenerator { - def generationState + protected Map docState - public JLPBaseEmitter(def generationState) { - this.generationState = generationState } + protected JLPBaseGenerator() { + docState = [orgs: [:], + currentDocId: false ] } - public String emitDocument(List sourceNodes) { + protected Map generate(Map> sources) { + Map result = [:] + sources.each { sourceId, sourceNodes -> + docState.currentDocId = sourceId + result[sourceId] = emitDocument(sourceNodes) } + return result } + + protected String emitDocument(List sourceNodes) { StringBuilder result = sourceNodes.inject(new StringBuilder()) { sb, node -> - sb.append(emit(node, generationState)) + sb.append(emit(node)) return sb } return result.toString() } @@ -24,15 +34,16 @@ public abstract class JLPBaseEmitter { printQueue = docBlock.directives.collect { directive -> def queueItem = [line: directive.lineNumber, value: directive] - switch (direcive.type) { - case DirectiveType.Author: queueItem.priority = 90; break + switch (directive.type) { + case DirectiveType.Author: queueItem.priority = 50; break case DirectiveType.Doc: queueItem.priority = 50; break case DirectiveType.Example: queueItem.priority = 50; break - case DirectiveType.Org: queueItem.priority = 10; break + case DirectiveType.Org: queueItem.priority = 10; break } + return queueItem } printQueue.addAll(docBlock.textBlocks.collect { textBlock -> - [ priority: 50, line: textBlock.lineNumber, value: textBlock ] } + [ priority: 50, line: textBlock.lineNumber, value: textBlock ] }) // sort by priority, then by line number printQueue.sort( @@ -45,7 +56,7 @@ public abstract class JLPBaseEmitter { sb.append(emit(printable.value)) return sb } - return result.toString() } + return result.toString() } protected abstract String emit(TextBlock textBlock) protected abstract String emit(Directive directive) diff --git a/src/main/com/jdblabs/jlp/JLPPegParser.java b/src/main/com/jdblabs/jlp/JLPPegParser.java index 8a367d7..630105f 100644 --- a/src/main/com/jdblabs/jlp/JLPPegParser.java +++ b/src/main/com/jdblabs/jlp/JLPPegParser.java @@ -45,7 +45,7 @@ public class JLPPegParser extends BaseParser { /** * Parses the rule: - * CodeBlock = !DOC_START RemainingLine + * CodeBlock = !DocStart RemainingLine * * Pushes a CodeBlock onto the stack. */ @@ -53,24 +53,29 @@ public class JLPPegParser extends BaseParser { return Sequence( push(curLineNum), push(""), - OneOrMore(FirstOf( - Sequence( - TestNot(DOC_START), RemainingLine(), - push(popAsString() + match())), - Sequence(EmptyLine(), - push(popAsString() + match())))), + OneOrMore(Sequence( + TestNot(DocStart()), RemainingLine(), + push(popAsString() + match()))), + push(makeCodeBlock(popAsString(),popAsInt()))); } + /** + * Parses the rule: + * DocStart = SPACE* DOC_START + */ + Rule DocStart() { + return Sequence(ZeroOrMore(SPACE), DOC_START); } + /** * Parses the rule: * DirectiveBlock = - * DOC_START DIRECTIVE_START (LongDirective / LineDirective) + * DocStart DIRECTIVE_START (LongDirective / LineDirective) * * Pushes a Directive onto the stack. */ Rule DirectiveBlock() { return Sequence( - DOC_START, DIRECTIVE_START, + DocStart(), DIRECTIVE_START, FirstOf(LongDirective(), LineDirective())); } /** @@ -129,13 +134,13 @@ public class JLPPegParser extends BaseParser { /** * Parses the rule: * DocTextLine = - * DOC_START !DIRECTIVE_START RemainingLine + * DocStart !DIRECTIVE_START RemainingLine * - * Pushes the line value (not including the DOC_START) onto the stack. + * Pushes the line value (not including the DocStart) onto the stack. */ Rule DocTextLine() { return Sequence( - DOC_START, TestNot(DIRECTIVE_START), + DocStart(), TestNot(DIRECTIVE_START), RemainingLine(), push(match())); } /** @@ -144,10 +149,13 @@ public class JLPPegParser extends BaseParser { */ @SuppressSubnodes Rule RemainingLine() { - return Sequence(OneOrMore(NOT_EOL), FirstOf(EOL, EOI), incLineCount()); } + return FirstOf( + Sequence(ZeroOrMore(NOT_EOL), EOL, incLineCount()), - Rule EmptyLine() { - return Sequence(EOL, incLineCount()); } + // allow EOI as a line delimiter only if the line is not empty, + // otherwise it will match infinitely if RemainingLine is used in a + // OneOrMore context. + Sequence(OneOrMore(NOT_EOL), EOI)); } Rule DOC_START = String("%% "); Rule EOL = Ch('\n'); diff --git a/src/main/com/jdblabs/jlp/JLPPegParser.java.noactions b/src/main/com/jdblabs/jlp/JLPPegParser.java.noactions deleted file mode 100644 index 2d14c1a..0000000 --- a/src/main/com/jdblabs/jlp/JLPPegParser.java.noactions +++ /dev/null @@ -1,133 +0,0 @@ -package com.jdblabs.jlp; - -import com.jdblabs.jlp.ast.*; -import java.util.ArrayList; -import java.util.List; -import org.parboiled.Action; -import org.parboiled.BaseParser; -import org.parboiled.Context; -import org.parboiled.Rule; -import org.parboiled.annotations.*; - -import static com.jdblabs.jlp.ast.TextBlock.makeCodeBlock; -import static com.jdblabs.jlp.ast.TextBlock.makeMarkdownBlock; - -@BuildParseTree -public class JLPPegParser extends BaseParser { - - public Rule CodePage() { - return ZeroOrMore(FirstOf( - DocBlock(), - CodeBlock())); } - - /** - * Parses the rule: - * DocBlock = DirectiveBlock / MarkdownBlock - * - * Pushes a DocBlock object onto the stack. - */ - Rule DocBlock() { - return OneOrMore(FirstOf( - DirectiveBlock(), - MarkdownBlock())); } - - /** - * Parses the rule: - * CodeBlock = !DOC_START RemainingLine - * - * Pushes a CodeBlock onto the stack. - */ - Rule CodeBlock() { - return Sequence( - TestNot(DOC_START), RemainingLine(), - ZeroOrMore(Sequence( - TestNot(DOC_START), RemainingLine()))); } - - /** - * Parses the rule: - * DirectiveBlock = - * DOC_START DIRECTIVE_START (LongDirective / LineDirective) - * - * Pushes a Directive onto the stack. - */ - Rule DirectiveBlock() { - return Sequence( - DOC_START, DIRECTIVE_START, - FirstOf(LongDirective(), LineDirective())); } - - /** - * Parses the rule: - * LongDirective = - * (AUTHOR_DIR / DOC_DIR / EXAMPLE_DIR) RemainingLine MarkdownBlock? - * - * Pushes a Directive object onto the value stack. - */ - Rule LongDirective() { - return Sequence( - FirstOf(AUTHOR_DIR, DOC_DIR, EXAMPLE_DIR), - RemainingLine(), - Optional(MarkdownBlock())); } - - /** - * Parses the rule: - * LineDirective = - * ORG_DIR RemainingLine - * - * Pushes a Directive object onto the value stack. - */ - Rule LineDirective() { - return Sequence( - ORG_DIR, - RemainingLine()); } - - /** - * Parses the rule: - * MarkdownBlock = MarkdownLine+ - * - * Pushes a MarkdownBlock onto the stack as a string. - */ - Rule MarkdownBlock() { return OneOrMore(MarkdownLine()); } - - /** - * Parses the rule: - * MarkdownLine = - * DOC_START !DIRECTIVE_START RemainingLine - * - * Pushes the line value (not including the DOC_START) onto the stack. - */ - Rule MarkdownLine() { - return Sequence( - DOC_START, TestNot(DIRECTIVE_START), RemainingLine()); } - - /** - * Parses the rule: - * RemainingLine = (!EOL)+ EOL - */ - @SuppressSubnodes - Rule RemainingLine() { - return Sequence(OneOrMore(NOT_EOL), EOL); } - - Rule DOC_START = String("%% "); - Rule EOL = FirstOf(Ch('\n'), EOI); - Rule NOT_EOL = Sequence(TestNot(EOL), ANY); - Rule DIRECTIVE_START= Ch('@'); - Rule SLASH = Ch('/'); - - // directive terminals - Rule AUTHOR_DIR = IgnoreCase("author"); - Rule DOC_DIR = IgnoreCase("doc"); - Rule EXAMPLE_DIR = IgnoreCase("example"); - Rule ORG_DIR = IgnoreCase("org"); - - String popAsString() { - return (String) pop(); } - - List addToList(ASTNode value, List list) { - list.add(value); - return list; } - - boolean printValueStack() { - for (int i = 0; i < getContext().getValueStack().size(); i++) { - System.out.println(i + ": " + peek(i)); } - return true; } -} diff --git a/src/main/com/jdblabs/jlp/MarkdownEmitter.groovy b/src/main/com/jdblabs/jlp/MarkdownEmitter.groovy deleted file mode 100644 index f038518..0000000 --- a/src/main/com/jdblabs/jlp/MarkdownEmitter.groovy +++ /dev/null @@ -1,23 +0,0 @@ -package com.jdblabs.jlp - -import com.jdblabs.jlp.ast.* -import org.pegdown.PegDownParser - -public class MarkdownEmitter extends JLPEmitter { - - protected MarkdownEmitter() {} - - def pegdown = new PegDownParser() - - protected String emitAuthor(String value) { - '${value}' } - - protected String emitDoc(String value) { /* parse as MD */ } - - protected String emitExample(String value) {/* parse as MD */ } - - protected String emitOrg(String value) { } - - protected String emitBlock(TextBlock textBlock) { "todo" } - -} diff --git a/src/main/com/jdblabs/jlp/MarkdownFormatter.groovy b/src/main/com/jdblabs/jlp/MarkdownFormatter.groovy deleted file mode 100644 index 91f45ba..0000000 --- a/src/main/com/jdblabs/jlp/MarkdownFormatter.groovy +++ /dev/null @@ -1,16 +0,0 @@ -package com.jdblabs.jlp - -public class MarkdownFormatter implements Formatter { - - private PegDownProcessor pegdown - - public MarkdownFormatter() { - pegdown = new PegDownProcessor() } - - public String formatText(String s) { pegdown.markdownToHtml(s) } - - public String formatCode(String s) { - pegdown.markdownToHtml(s.replaceAll(/(^|\n)/, /$1 /)) } - - public String formatReference(String s) { '' } -} diff --git a/src/main/com/jdblabs/jlp/MarkdownGenerator.groovy b/src/main/com/jdblabs/jlp/MarkdownGenerator.groovy new file mode 100644 index 0000000..862f1f4 --- /dev/null +++ b/src/main/com/jdblabs/jlp/MarkdownGenerator.groovy @@ -0,0 +1,64 @@ +package com.jdblabs.jlp + +import com.jdblabs.jlp.ast.* +import com.jdblabs.jlp.ast.Directive.DirectiveType +import com.jdblabs.jlp.ast.TextBlock.TextBlockType + +import org.pegdown.PegDownProcessor +import org.pegdown.Extensions + +public class MarkdownGenerator extends JLPBaseGenerator { + + protected PegDownProcessor pegdown + + protected MarkdownGenerator() { + super() + + pegdown = new PegDownProcessor(Extensions.TABLES) } + + protected static Map generateDocuments( + Map> sources) { + MarkdownGenerator inst = new MarkdownGenerator() + return inst.generate(sources) } + + protected String emit(TextBlock textBlock) { + switch (textBlock.type) { + + // text block, just convert to markdown + case TextBlockType.TextBlock: + return formatText(textBlock.value) + + // code block, we want to emit as a code snippet + case TextBlockType.CodeBlock: + // so prepend all lines with four spaces to tell markdown that + // this is code + String value = textBlock.value.replaceAll(/(^|\n)/, /$1 /) + // then convert to markdown + return pegdown.markdownToHtml(value) } } + + protected String emit(Directive d) { + switch (d.type) { + case DirectiveType.Author: + return "Author: ${formatText(d.value)}" + case DirectiveType.Doc: + return formatText(d.value) + case DirectiveType.Example: + return formatText(d.value) + case DirectiveType.Org: + docState.orgs[d.value] = [line: d.lineNumber, + file: docState.currentDocId] + return "" } + } + + protected String formatText(String text) { + + // convert to HTML from Markdown + String md = pegdown.markdownToHtml(text) + + // replace internal `jlp://` links with actual links based on`@org` + // references + md = md.replaceAll(/jlp:\/\/([^\s"])/, /#$1/) + + return md; + } +} diff --git a/src/main/com/jdblabs/jlp/ParserActions.groovy b/src/main/com/jdblabs/jlp/ParserActions.groovy deleted file mode 100644 index 7a3485c..0000000 --- a/src/main/com/jdblabs/jlp/ParserActions.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package com.jdblabs.jlp - -import org.parboiled.Action - -public class ParserActions { - -} diff --git a/src/main/com/jdblabs/jlp/TransparentFormatter.groovy b/src/main/com/jdblabs/jlp/TransparentFormatter.groovy deleted file mode 100644 index aaa5af2..0000000 --- a/src/main/com/jdblabs/jlp/TransparentFormatter.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package com.jdblabs.jlp - -public class TransparentFormatter implements Formatter { - - public String formatText(String text) { return text } - public String formatCode(String code) { return code } - public String formatReference(String ref) { return "ref#${ref}" } } diff --git a/src/main/com/jdblabs/jlp/TransparentGenerator.groovy b/src/main/com/jdblabs/jlp/TransparentGenerator.groovy new file mode 100644 index 0000000..802cda5 --- /dev/null +++ b/src/main/com/jdblabs/jlp/TransparentGenerator.groovy @@ -0,0 +1,24 @@ +package com.jdblabs.jlp + +import com.jdblabs.jlp.ast.* +import com.jdblabs.jlp.ast.Directive.DirectiveType +import java.util.List +import java.util.Map + +public class TransparentGenerator extends JLPBaseGenerator { + + protected TransparentGenerator() {} + + public static Map generateDocuments(Map> sources) { + TransparentGenerator inst = new TransparentGenerator() + return inst.generate(sources) } + + protected String emit(TextBlock textBlock) { textBlock.value } + protected String emit(Directive directive) { + switch (directive.type) { + case DirectiveType.Author: return "Author: ${directive.value}\n" + case DirectiveType.Doc: return directive.value + case DirectiveType.Example: return "Example: ${directive.value}" + case DirectiveType.Org: return "" } } +}