diff --git a/resources/main/test.groovy b/resources/main/test.groovy index 628bb1a..5f97efe 100644 --- a/resources/main/test.groovy +++ b/resources/main/test.groovy @@ -3,28 +3,36 @@ import org.parboiled.Parboiled import org.parboiled.parserunners.ReportingParseRunner import org.parboiled.parserunners.RecoveringParseRunner -parser = Parboiled.createParser(JLPPegParser.class) -parseRunner = new RecoveringParseRunner(parser.SourceFile()) +makeParser = { + println "Making the standard parser." + println "---------------------------" + parser = Parboiled.createParser(JLPPegParser.class) + parseRunner = new ReportingParseRunner(parser.SourceFile()) +} + +makeExperimentalParser = { + println "Making the experimental parser." + println "-------------------------------" + + parser = Parboiled.createParser(com.jdblabs.jlp.experimental.JLPPegParser.class) + parseRunner = new ReportingParseRunner(parser.SourceFile()) +} simpleTest = { - "Parsing the simple test into 'result'.\n" + - "--------------------------------------\n" + println "Parsing the simple test into 'result'." + println "--------------------------------------" testLine = """%% This the first test line. %% Second Line - %% Third Line - Fourth line + %% Third Line \n\n Fifth line \n\n %% Seventh line \n\n + %% @author Eigth Line + %% @Example Ninth Line + %% Markdown lines (tenth line) + %% Still markdown (eleventh line) + Twelfth 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) + simpleResult = parseRunner.run(testLine) } vbsTest = { diff --git a/resources/main/vbs_db_records.hrl b/resources/main/vbs_db_records.hrl index 58fe73a..303d5d9 100644 --- a/resources/main/vbs_db_records.hrl +++ b/resources/main/vbs_db_records.hrl @@ -1,8 +1,7 @@ % vbs_db_records.erl %% @author Jonathan Bernard -%% #copyright 2010-2011 JDB Labs Inc. +%% @copyright 2010-2011 JDB Labs Inc. -%% @doc %% The VBS database API is centered around the data records: %% %% * Tables are named after the records. ``vbs_adult`` records are stored in @@ -15,30 +14,30 @@ %% #Record Definitions %% Here are the record definitions: -%% @doc Information about an adult in the VBS system. +%% @api 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 + %% @api 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. + %% @api A unique full name. %% @example "John Smith", "Fae Alice McDonald" %% @org records/vbs_adult/name name, - %% @doc The adult's age (optional). + %% @api The adult's age (optional). %% @org records/vbs_adult/age age = 0, - %% @doc A list of phone numbers (strings). + %% @api 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, + %% @api The adult's address (optional). There is not pre-defined format, %% this is a string that can be formatted s desired (linebreaks are ok, %% for example). %% @example @@ -48,28 +47,28 @@ %% @org records/vbs_adult/address address = "", - %% @doc The adult's email address as a string. + %% @api 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. +%% @api 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 + %% @api 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 + %% @api The id of person who attended. This is a foreign key onto either the %% [`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 + %% @api The type of person who attended. This determines which table the %% [`person_id`](jlp://records/vbs_attendance/person_id) links on. The %% possible values and the corresponding link tables are: %% @@ -81,23 +80,23 @@ %% @org records/vbs_attendance/person_type person_type, - %% @doc The date of attendance, stored as {Year, Month, Day}. + %% @api 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 + %% @api 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 + %% @api 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 + %% @api A list of {Key, Value} pairs that can be used to store additional %% information. This is intended to allow callers to store optional data, %% or client-specific data, without having to alter the database schema. %% When working with `vbs_attendance` records, a caller should ignore @@ -105,196 +104,196 @@ %% @org records/vbs_attendance/ext_data ext_data = [], - %% @doc Any comments for the day about this person. + %% @api Any comments for the day about this person. %% @org records/vbs_attendance/comments comments = ""}). -%% @doc Information about a child in the VBS program. +%% @api 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 + %% @api 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 + %% @api The id of the crew to which this child has been assigned. This is a %% 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. + %% @api 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} + %% @api 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` + %% @api The child's gender, either `male` or `female` %% @org records/vbs_child/gender gender, - %% @doc The child's grade level in school. + %% @api 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 + %% @api A list of ids representing the child's legal guardians. These link %% the child record to adult records by the %% [`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 + %% @api 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`](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 + %% @api 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`](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 + %% @api 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, + %% @api 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 + %% @api 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} + %% @api 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. + %% @api 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. + %% @api 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. + %% @api Any known allergies this child has. %% @org records/vbs_child/allergies allergies, - %% @doc Additional comments about this child. + %% @api Additional comments about this child. %% @org records/vbs_child/comments comments}). -%% @doc Information about a crew in the VBS system. +%% @api 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 + %% @api 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. + %% @api The crew number. %% @org records/vbs_crew/number number, - %% @doc The crew type. This is a foreign key on + %% @api The crew type. This is a foreign key on %% [`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. + %% @api The name of the crew, stored as a string. %% @org records/vbs_crew/name name, - %% @doc Any comments about the crew. + %% @api 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 +%% @api 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 + %% @api 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. + %% @api 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 +%% @api 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 + %% @api A name for the counter. This is the primary key for the table and %% must be unique. %% @example `vbs_adult_id` %% @org records/vbs_id_counter/name name, - %% @doc The next value for this counter. + %% @api 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. +%% @api 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 + %% @api 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`](jlp://records/vbs_adult) + %% @api 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 + %% @api The crew this worker is assigned to. This is a link to %% [`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 + %% @api %% @org records/vbs_worker/worker_type_id worker_type_id, % foreign key on worker_type - %% @doc + %% @api %% @org records/vbs_worker/shirt_size shirt_size, - %% @doc + %% @api %% @org records/vbs_worker/ext_data ext_data = []}). -%% @doc +%% @api %% @org records/vbs_worker_type -record(vbs_worker_type, { - %% @doc + %% @api %% @org records/vbs_worker_type/id id, - %% @doc + %% @api %% @org records/vbs_worker_type/name name}). diff --git a/src/main/com/jdblabs/jlp/experimental/DeadSimple.java b/src/main/com/jdblabs/jlp/experimental/DeadSimple.java new file mode 100644 index 0000000..e763d94 --- /dev/null +++ b/src/main/com/jdblabs/jlp/experimental/DeadSimple.java @@ -0,0 +1,23 @@ +package com.jdblabs.jlp.experimental; + +import org.parboiled.Action; +import org.parboiled.BaseParser; +import org.parboiled.Context; +import org.parboiled.Rule; +import org.parboiled.annotations.*; + +@BuildParseTree +public class DeadSimple extends BaseParser { + + public Rule S() { + return OneOrMore(FirstOf(Line(), EmptyLine()).skipNode()); } + + public Rule Line() { + return Sequence(OneOrMore(NOT_EOL).skipNode(), EOL); } + + public Rule EmptyLine() { + return EOL; } + + public Rule EOL = Ch('\n'); + public Rule NOT_EOL = Sequence(TestNot(EOL), ANY); +} diff --git a/src/main/com/jdblabs/jlp/experimental/JLPBaseGenerator.groovy b/src/main/com/jdblabs/jlp/experimental/JLPBaseGenerator.groovy new file mode 100644 index 0000000..0642efc --- /dev/null +++ b/src/main/com/jdblabs/jlp/experimental/JLPBaseGenerator.groovy @@ -0,0 +1,37 @@ +package com.jdblabs.jlp.experimental + +import com.jdblabs.jlp.experimental.ast.* +import java.util.List +import java.util.Map + +public abstract class JLPBaseGenerator { + + protected Map docState + + protected JLPBaseGenerator() { + docState = [orgs: [:], // stores `@org` references + codeTrees: [:], // stores code ASTs for + currentDocId: false ] } // store the current docid + + protected Map generate(Map sources) { + Map result = [:] + sources.each { sourceId, sourceAST -> + + // set up the current generator state for this source + docState.currentDocId = sourceId + docState.codeTrees[sourceId] = sourceAST.codeAST + + // generate the doc for this source + result[sourceId] = emit(sourceAST) } + + // return our results + return result } + + protected abstract String emit(SourceFile sourceFile) + protected abstract String emit(Block block) + protected abstract String emit(DocBlock docBlock) + protected abstract String emit(CodeBlock codeBlock) + protected abstract String emit(DocText docText) + protected abstract String emit(Directive directive) + +} diff --git a/src/main/com/jdblabs/jlp/experimental/JLPPegParser.java b/src/main/com/jdblabs/jlp/experimental/JLPPegParser.java index 5f75a43..5016878 100644 --- a/src/main/com/jdblabs/jlp/experimental/JLPPegParser.java +++ b/src/main/com/jdblabs/jlp/experimental/JLPPegParser.java @@ -22,13 +22,67 @@ public class JLPPegParser extends BaseParser { */ public Rule SourceFile() { return Sequence( + // At the start of processing a new SourceFile, we need to set up + // our internal state. + + // Clear the line count. clearLineCount(), + + // Add the top-level SourceFile AST node. push(new SourceFile()), + // A SourceFile is made up of one or more Blocks OneOrMore(Sequence( - FirstOf(Block(), DocBlock(), CodeBlock()), + // All of these options result in one new Block pushed onto the + // stack. + FirstOf( - addBlock((ASTNode) pop())))); } + // Match a whole Block. This pushes a Block on the stack. + Block(), + + // A standalone DocBlock. We will create an empty CodeBlock + // to pair with it, then push a new Block onto the stack + // made from the DocBlock and the empty CodeBlock + Sequence( + // 1. We need to remember the line number to create the + // Block + push(curLineNum), + + // 2. Match the DocBlock. + DocBlock(), + + // 3. Create the empty CodeBlock. + push(new CodeBlock(curLineNum)), + + // 4. Create the Block and push it onto the stack. + push(new Block((CodeBlock) pop(), (DocBlock) pop(), + popAsInt()))), + + // A standalone CodeBlock. Similar to the standalone + // DocBlock, we will create an empty DocBlock to pair with + // the CodeBlock to make a Block, which is pushed onto the + // stack: + // + // *Note: With the way the parser is currently written, + // this will only match a CodeBlock that occurs + // before any DocBlock.* + Sequence( + // 1. Remember the line number for the Block. + push(curLineNum), + + // 2. Create the empty DocBlock. + push(new DocBlock(curLineNum)), + + // 3. Match the CodeBlock + CodeBlock(), + + // 4. Create the Block and push it onto the stack + push(new Block((CodeBlock) pop(), (DocBlock) pop(), + popAsInt())))), + + // pop the Block created by one of the above options and add it + // to the SourceFile + addBlockToSourceFile((Block) pop())))); } /** * Parses the rule: @@ -71,7 +125,7 @@ public class JLPPegParser extends BaseParser { /** * Parses the rule: - * Directive = DocLineStart AT (LongDirective / ShortFirective) + * Directive = DocLineStart AT (LongDirective / ShortDirective) * * Pushes a Directive node on the stack. */ @@ -82,7 +136,7 @@ public class JLPPegParser extends BaseParser { /** * Parses the rule: * LongDirective = - * (AUTHOR_DIR / DOC_DIR / EXAMPLE_DIR) RemainingLine DocText? + * (API_DIR / EXAMPLE_DIR) RemainingLine DocText? * * Pushes a Directive node onto the stack. */ @@ -90,7 +144,7 @@ public class JLPPegParser extends BaseParser { return Sequence( push(curLineNum), - FirstOf(AUTHOR_DIR, DOC_DIR, EXAMPLE_DIR), push(match()), + FirstOf(API_DIR, EXAMPLE_DIR), push(match()), RemainingLine(), push(match()), Optional(Sequence( @@ -102,14 +156,14 @@ public class JLPPegParser extends BaseParser { /** * Parses the rule: - * ShortDirective = (ORG_DIR / COPYRIGHT_DIR) RemainingLine + * ShortDirective = (AUTHOR_DIR / ORG_DIR / COPYRIGHT_DIR) RemainingLine * * Pushes a Directive node onto the stack. */ Rule ShortDirective() { return Sequence( push(curLineNum), - FirstOf(ORG_DIR, COPYRIGHT_DIR), push(match()), + FirstOf(AUTHOR_DIR, ORG_DIR, COPYRIGHT_DIR), push(match()), RemainingLine(), push(new Directive(match().trim(), popAsString(), popAsInt()))); } @@ -127,27 +181,28 @@ public class JLPPegParser extends BaseParser { DocLineStart(), TestNot(AT), RemainingLine(), addToDocText(match())))); } - @SuppressSubnodes Rule DocLineStart() { return Sequence( ZeroOrMore(SPACE), DOC_LINE_START, Optional(SPACE)); } - @SuppressSubnodes + Rule NonEmptyLine() { + return Sequence(OneOrMore(NOT_EOL), FirstOf(EOL, EOI)); } + Rule RemainingLine() { return FirstOf( Sequence(ZeroOrMore(NOT_EOL), EOL, incLineCount()), Sequence(OneOrMore(NOT_EOL), EOI, incLineCount())); } - Rule AT = Ch('@'); - Rule EOL = OneOrMore(AnyOf("\r\n")); - Rule NOT_EOL = Sequence(TestNot(EOL), ANY); - Rule SPACE = AnyOf(" \t"); - Rule DOC_LINE_START = String("%%"); + Rule AT = Ch('@').label("AT"); + Rule EOL = FirstOf(String("\r\n"), Ch('\n'), Ch('\r')).label("EOL"); + Rule NOT_EOL = Sequence(TestNot(EOL), ANY).label("NOT_EOL"); + Rule SPACE = AnyOf(" \t").label("SPACE"); + Rule DOC_LINE_START = String("%%").label("DOC_LINE_START"); // directive terminals Rule AUTHOR_DIR = IgnoreCase("author"); Rule COPYRIGHT_DIR = IgnoreCase("copyright"); - Rule DOC_DIR = IgnoreCase("doc"); + Rule API_DIR = IgnoreCase("api"); Rule EXAMPLE_DIR = IgnoreCase("example"); Rule ORG_DIR = IgnoreCase("org"); @@ -159,7 +214,7 @@ public class JLPPegParser extends BaseParser { boolean incLineCount() { curLineNum++; return true; } - boolean addBlock(ASTNode block) { + boolean addBlockToSourceFile(Block block) { SourceFile sourceFile = (SourceFile) pop(); sourceFile.blocks.add(block); return push(sourceFile); } diff --git a/src/main/com/jdblabs/jlp/experimental/LiterateMarkdownGenerator.groovy b/src/main/com/jdblabs/jlp/experimental/LiterateMarkdownGenerator.groovy new file mode 100644 index 0000000..787e058 --- /dev/null +++ b/src/main/com/jdblabs/jlp/experimental/LiterateMarkdownGenerator.groovy @@ -0,0 +1,171 @@ +package com.jdblabs.jlp.experimental + +import com.jdblabs.jlp.experimental.ast.* +import com.jdblabs.jlp.experimental.ast.Directive.DirectiveType + +import org.pegdown.Extensions +import org.pegdown.PegDownProcessor + +import java.util.List + +public class LiterateMarkdownGenerator extends JLPBaseGenerator { + + protected PegDownProcessor pegdown + + protected LiterateMarkdownGenerator() { + super() + + pegdown = new PegDownProcessor( + Extensions.TABLES & Extensions.DEFINITIONS) } + + protected static Map generateDocuments( + Map sources) { + LiterateMarkdownGenerator inst = new LiterateMarkdownGenerator() + return inst.generate(sources) } + + protected String emit(SourceFile sourceFile) { + StringBuilder sb = new StringBuilder() + + + // Create the HTML file head + sb.append( +""" + + + ${docState.currentDocId} + + + + +
+ + + + + """) + + // Emit the document to Markdown + sourceFile.blocks.each { block -> sb.append(emit(block)) } + + // Create the HTML file foot + sb.append( +""" +

${docState.currentDocId}

+
+
+ +""") + + return sb.toString() } + + protected String emit(Block block) { + StringBuilder sb = new StringBuilder() + + // Look for an `@org` directive in the `Block` + Directive orgDir = block.docBlock.directives.find { + it.type == DirectiveType.Org } + + // Create the `tr` that will hold the `Block` + if (orgDir) { sb.append("\n") } + else { sb.append("") } + + // Create the `td` for the documentation. + sb.append('\n') + sb.append(emit(block.docBlock)) + sb.append('') + + // Create the `td` for the `CodeBlock` + sb.append('\n') + sb.append(emit(block.codeBlock)) + sb.append('') + + // Close the table row. + sb.append('') } + + protected String emit(DocBlock docBlock) { + // Create a queue for the doc block elements, we are going to + // sort them by type and line number + List emitQueue + + // Later we will need a string builder to hold our result. + StringBuilder sb + + // Add all the directives + emitQueue = docBlock.directives.collect { directive -> + def queueItem = [lineNumber: directive.lineNumber, value: directive] + switch(directive.type) { + case DirectiveType.Api: queueItem.priority = 12; break + case DirectiveType.Author: queueItem.priority = 10; break + case DirectiveType.Copyright: queueItem.priority = 11; break + case DirectiveType.Example: queueItem.priority = 50; break + case DirectiveType.Org: + docState.orgs[directive.value] = directive + queueItem.priority = 0 + break } + + return queueItem } + + // Add all the doc text blocks + emitQueue.addAll(docBlock.docTexts.collect { docText -> + [lineNumber: docText.lineNumber, priority: 50, value: docText] }) + + + println emitQueue + println "----------" + + // Sort the emit queue by priority, then line number. + emitQueue.sort( + {i1, i2 -> i1.priority != i2.priority ? + i1.priority - i2.priority : + i1.line - i2.line} as Comparator) + + // Finally, we want to treat the whole block as one markdown chunk, so + // we will concatenate the values in the emit queue and then process + // the whole block once + sb = new StringBuilder() + emitQueue.each { queueItem -> sb.append(emit(queueItem.value)) } + + return pegdown.markdownToHtml(sb.toString()) + } + + protected String emit(CodeBlock codeBlock) { + def codeLines + + // Collect the lines into an array. + codeLines = codeBlock.lines.collect { lineNumber, line -> + [lineNumber, line] } + + // Sort by line number. + codeLines.sort({ i1, i2 -> i1[0] - i2[0] } as Comparator) + + codeLines = codeLines.collect { arr -> arr[1] } + + // write out the lines in a
 block
+        return "
${codeLines.join('')}
" } + + protected String emit(DocText docText) { return docText.value } + + protected String emit(Directive directive) { + switch(directive.type) { + + // An `@api` directive is immediately processed and wrapped in a + // div (we need to process this now because Markdown does not + // process input inside HTML elements). + case DirectiveType.Api: + return "
" + + pegdown.markdownToHtml(directive.value) + "
\n" + + // An `@author` directive is turned into a definition list. + case DirectiveType.Author: + return "Author\n: ${directive.value}\n" + + case DirectiveType.Copyright: + return "© ${directive.value}\n" + + // An `@example` directive is returned as is + case DirectiveType.Example: return directive.value + + // An `@org` directive is ignored. + case DirectiveType.Org: return "" } + } +} diff --git a/src/main/com/jdblabs/jlp/experimental/ast/Directive.groovy b/src/main/com/jdblabs/jlp/experimental/ast/Directive.groovy index 758c315..4de3c86 100644 --- a/src/main/com/jdblabs/jlp/experimental/ast/Directive.groovy +++ b/src/main/com/jdblabs/jlp/experimental/ast/Directive.groovy @@ -3,9 +3,9 @@ package com.jdblabs.jlp.experimental.ast public class Directive extends ASTNode { public static enum DirectiveType { + Api, Author, Copyright, - Doc, Example, Org; diff --git a/src/main/com/jdblabs/jlp/experimental/ast/DocText.groovy b/src/main/com/jdblabs/jlp/experimental/ast/DocText.groovy index f9f9c52..8fb75a9 100644 --- a/src/main/com/jdblabs/jlp/experimental/ast/DocText.groovy +++ b/src/main/com/jdblabs/jlp/experimental/ast/DocText.groovy @@ -4,7 +4,9 @@ public class DocText extends ASTNode { public String value - public DocText(int lineNumber) { super(lineNumber) } + public DocText(int lineNumber) { + super(lineNumber) + value = "" } public String toString() { value } } diff --git a/src/main/com/jdblabs/jlp/experimental/ast/SourceFile.groovy b/src/main/com/jdblabs/jlp/experimental/ast/SourceFile.groovy index 4f2d0ba..b77517b 100644 --- a/src/main/com/jdblabs/jlp/experimental/ast/SourceFile.groovy +++ b/src/main/com/jdblabs/jlp/experimental/ast/SourceFile.groovy @@ -3,4 +3,6 @@ package com.jdblabs.jlp.experimental.ast public class SourceFile { public List blocks = [] public def codeAST + + public String id }