Initialize fr.feufochmar.scribbly library.
This commit is contained in:
commit
ff69792292
|
@ -0,0 +1 @@
|
|||
target/
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>fr.feufochmar.scribbly</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.m2e.core.maven2Builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.m2e.core.maven2Nature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,40 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>fr.feufochmar</groupId>
|
||||
<artifactId>fr.feufochmar.scribbly</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>Scribbly document format.</name>
|
||||
<description>Scribbly is a document format inspired by the syntax of Racket's Scribble.</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>4.12.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.antlr</groupId>
|
||||
<artifactId>antlr4-maven-plugin</artifactId>
|
||||
<version>4.12.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>antlr</id>
|
||||
<goals>
|
||||
<goal>antlr4</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,13 @@
|
|||
lexer grammar ScribblyLexer;
|
||||
|
||||
ARROBE : '@' ;
|
||||
LBLOCK : '[' ;
|
||||
RBLOCK : ']' ;
|
||||
LCURLY : '{' ;
|
||||
RCURLY : '}' ;
|
||||
COLON : ':' ;
|
||||
|
||||
WS: [ \t\n\r\f]+ -> skip ;
|
||||
ID: [a-z]+ ;
|
||||
|
||||
STRING: (~('@' | '[' | ']' | '{' | '}'))+ ;
|
|
@ -0,0 +1,40 @@
|
|||
parser grammar ScribblyParser;
|
||||
options { tokenVocab=ScribblyLexer; }
|
||||
|
||||
document
|
||||
: item* EOF
|
||||
;
|
||||
|
||||
item
|
||||
: text
|
||||
| command
|
||||
;
|
||||
|
||||
command
|
||||
: ARROBE commandName WS* (LBLOCK attributeKey COLON attributeValue+ RBLOCK WS*)* LCURLY commandVal* RCURLY
|
||||
;
|
||||
|
||||
commandName
|
||||
: ID
|
||||
;
|
||||
|
||||
attributeKey
|
||||
: ID
|
||||
;
|
||||
|
||||
attributeValue
|
||||
: ID
|
||||
| STRING
|
||||
| ARROBE (ARROBE | LBLOCK | RBLOCK | LCURLY | RCURLY)
|
||||
;
|
||||
|
||||
commandVal
|
||||
: text
|
||||
| command
|
||||
;
|
||||
|
||||
text
|
||||
: ID
|
||||
| STRING
|
||||
| ARROBE (ARROBE | LBLOCK | RBLOCK | LCURLY | RCURLY)
|
||||
;
|
|
@ -0,0 +1,60 @@
|
|||
package fr.feufochmar.scribbly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ScribblyCommand implements ScribblyElement
|
||||
{
|
||||
private String m_identifier;
|
||||
private final Map<String, List<ScribblyText>> m_attributes = new HashMap<String, List<ScribblyText>>();
|
||||
private final List<ScribblyElement> m_values = new ArrayList<ScribblyElement>();
|
||||
|
||||
public ScribblyCommand()
|
||||
{
|
||||
}
|
||||
|
||||
public void setIdentifier(String identifier)
|
||||
{
|
||||
m_identifier = identifier;
|
||||
}
|
||||
|
||||
public String getIdentifier()
|
||||
{
|
||||
return m_identifier;
|
||||
}
|
||||
|
||||
public void addAttribute(String name, List<ScribblyText> value)
|
||||
{
|
||||
m_attributes.put(name, value);
|
||||
}
|
||||
|
||||
public List<ScribblyText> getAttribute(String name)
|
||||
{
|
||||
return m_attributes.getOrDefault(name, Collections.emptyList());
|
||||
}
|
||||
|
||||
public void addValue(ScribblyElement value)
|
||||
{
|
||||
m_values.add(value);
|
||||
}
|
||||
|
||||
public List<ScribblyElement> getValues()
|
||||
{
|
||||
return m_values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ScribblyVisitor visitor)
|
||||
{
|
||||
visitor.onBeginCommand(this);
|
||||
for (ScribblyElement elem: m_values)
|
||||
{
|
||||
elem.visit(visitor);
|
||||
}
|
||||
visitor.onEndCommand(this);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package fr.feufochmar.scribbly;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A Scribbly document.
|
||||
* @author feufochmar
|
||||
*
|
||||
*/
|
||||
public class ScribblyDocument
|
||||
{
|
||||
/**
|
||||
* Elements of the document
|
||||
*/
|
||||
private List<ScribblyElement> m_elements;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ScribblyDocument()
|
||||
{
|
||||
}
|
||||
|
||||
public void addElement(ScribblyElement elem)
|
||||
{
|
||||
m_elements.add(elem);
|
||||
}
|
||||
|
||||
public List<ScribblyElement> elements()
|
||||
{
|
||||
return m_elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor pattern method.
|
||||
* @param visitor
|
||||
*/
|
||||
public void visit(ScribblyVisitor visitor)
|
||||
{
|
||||
visitor.onBeginDocument(this);
|
||||
for (ScribblyElement elem: m_elements)
|
||||
{
|
||||
elem.visit(visitor);
|
||||
}
|
||||
visitor.onEndDocument(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
package fr.feufochmar.scribbly;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
import org.antlr.v4.runtime.tree.ErrorNode;
|
||||
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||
|
||||
import fr.feufochmar.scribbly.ScribblyParser.AttributeKeyContext;
|
||||
import fr.feufochmar.scribbly.ScribblyParser.AttributeValueContext;
|
||||
import fr.feufochmar.scribbly.ScribblyParser.CommandContext;
|
||||
import fr.feufochmar.scribbly.ScribblyParser.CommandNameContext;
|
||||
import fr.feufochmar.scribbly.ScribblyParser.CommandValContext;
|
||||
import fr.feufochmar.scribbly.ScribblyParser.DocumentContext;
|
||||
import fr.feufochmar.scribbly.ScribblyParser.ItemContext;
|
||||
import fr.feufochmar.scribbly.ScribblyParser.TextContext;
|
||||
|
||||
|
||||
/**
|
||||
* A scribbly document builder. Implements the ScribblyParserListener to construct the document content.
|
||||
* @author feufochmar
|
||||
*
|
||||
*/
|
||||
public class ScribblyDocumentParser implements ScribblyParserListener
|
||||
{
|
||||
/**
|
||||
* Current context of visited terminal
|
||||
* @author Feufochmar
|
||||
*/
|
||||
private enum Context
|
||||
{
|
||||
DOCUMENT,
|
||||
ITEM,
|
||||
COMMAND,
|
||||
COMMAND_NAME,
|
||||
COMMAND_ATTRIBUTE_KEY,
|
||||
COMMAND_ATTRIBUTE_VALUE,
|
||||
COMMAND_VALUE,
|
||||
TEXT
|
||||
}
|
||||
private final Deque<Context> m_currentContext = new LinkedList<Context>();
|
||||
|
||||
/**
|
||||
* Document built.
|
||||
*/
|
||||
private final ScribblyDocument m_document = new ScribblyDocument();
|
||||
|
||||
/**
|
||||
* Current command.
|
||||
*/
|
||||
private final Deque<ScribblyCommand> m_currentCommand = new LinkedList<ScribblyCommand>();
|
||||
|
||||
/**
|
||||
* Current attribute key.
|
||||
*/
|
||||
private String m_currentAttributeKey = "";
|
||||
|
||||
/**
|
||||
* Current attribute value.
|
||||
*/
|
||||
private final List<ScribblyText> m_currentAttributeValue = new ArrayList<ScribblyText>();
|
||||
|
||||
/**
|
||||
* Is escaping a character ?
|
||||
*/
|
||||
private boolean m_isEscaping = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ScribblyDocumentParser()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTerminal(TerminalNode node)
|
||||
{
|
||||
// Current context shouldn't be empty
|
||||
if (m_currentContext.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
String text = node.getText();
|
||||
switch (m_currentContext.peekFirst())
|
||||
{
|
||||
case COMMAND:
|
||||
// If terminal is ']', it's the end of an attribute => add the attribute and reset the temporary data
|
||||
if (node.getSymbol().getType() == ScribblyParser.RBLOCK)
|
||||
{
|
||||
// Set the attribute value
|
||||
m_currentCommand.peekFirst().addAttribute(m_currentAttributeKey, m_currentAttributeValue);
|
||||
// Reset the temporary data
|
||||
m_currentAttributeKey = "";
|
||||
m_currentAttributeValue.clear();
|
||||
}
|
||||
// Ignore other terminals of commands (they are part of the syntax of commands)
|
||||
break;
|
||||
case COMMAND_ATTRIBUTE_KEY:
|
||||
m_currentAttributeKey = text;
|
||||
break;
|
||||
case COMMAND_ATTRIBUTE_VALUE:
|
||||
if (!m_isEscaping && node.getSymbol().getType() == ScribblyParser.ARROBE)
|
||||
{
|
||||
m_isEscaping = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentAttributeValue.add(new ScribblyText(text));
|
||||
m_isEscaping = false;
|
||||
}
|
||||
break;
|
||||
case COMMAND_NAME:
|
||||
m_currentCommand.peekFirst().setIdentifier(text);
|
||||
break;
|
||||
case COMMAND_VALUE:
|
||||
// Ignore terminals of command value (should be text or commands)
|
||||
break;
|
||||
case DOCUMENT:
|
||||
// Ignore terminals of document (can only be text or EOF)
|
||||
break;
|
||||
case ITEM:
|
||||
// Ignore terminals of items (should be text or commands)
|
||||
break;
|
||||
case TEXT:
|
||||
if (!m_isEscaping && node.getSymbol().getType() == ScribblyParser.ARROBE)
|
||||
{
|
||||
m_isEscaping = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ScribblyText sText = new ScribblyText(text);
|
||||
if (m_currentCommand.isEmpty())
|
||||
{
|
||||
m_document.addElement(sText);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentCommand.peekFirst().addValue(sText);
|
||||
}
|
||||
m_isEscaping = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitErrorNode(ErrorNode node)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
// Throw exception ?
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterEveryRule(ParserRuleContext ctx)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitEveryRule(ParserRuleContext ctx)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterDocument(DocumentContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.DOCUMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitDocument(DocumentContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterItem(ItemContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.ITEM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitItem(ItemContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterCommand(CommandContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.COMMAND);
|
||||
ScribblyCommand command = new ScribblyCommand();
|
||||
if (m_currentCommand.isEmpty())
|
||||
{
|
||||
m_document.addElement(command);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentCommand.peekFirst().addValue(command);
|
||||
}
|
||||
m_currentCommand.addFirst(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitCommand(CommandContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
m_currentCommand.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterCommandName(CommandNameContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.COMMAND_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitCommandName(CommandNameContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterAttributeKey(AttributeKeyContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.COMMAND_ATTRIBUTE_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitAttributeKey(AttributeKeyContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterAttributeValue(AttributeValueContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.COMMAND_ATTRIBUTE_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitAttributeValue(AttributeValueContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterCommandVal(CommandValContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.COMMAND_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitCommandVal(CommandValContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterText(TextContext ctx)
|
||||
{
|
||||
m_currentContext.addFirst(Context.TEXT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitText(TextContext ctx)
|
||||
{
|
||||
m_currentContext.removeFirst();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package fr.feufochmar.scribbly;
|
||||
|
||||
/**
|
||||
* Base Interface for Scribbly elements.
|
||||
* @author feufochmar
|
||||
*
|
||||
*/
|
||||
public interface ScribblyElement
|
||||
{
|
||||
/**
|
||||
* Visitor pattern interface.
|
||||
*/
|
||||
void visit(ScribblyVisitor visitor);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package fr.feufochmar.scribbly;
|
||||
|
||||
/**
|
||||
* Represent text parts of Scribbly documents
|
||||
* @author feufochmar
|
||||
*/
|
||||
public class ScribblyText implements ScribblyElement
|
||||
{
|
||||
private String m_value = "";
|
||||
|
||||
public ScribblyText()
|
||||
{
|
||||
}
|
||||
|
||||
public ScribblyText(String value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
public void setValue(String value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(ScribblyVisitor visitor)
|
||||
{
|
||||
visitor.onText(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package fr.feufochmar.scribbly;
|
||||
|
||||
/**
|
||||
* Visitor pattern interface for Scribbly documents
|
||||
* @author feufochmar
|
||||
*
|
||||
*/
|
||||
public interface ScribblyVisitor
|
||||
{
|
||||
/**
|
||||
* Notify the beginning of a Scribbly document
|
||||
* @param doc Document visited.
|
||||
*/
|
||||
void onBeginDocument(ScribblyDocument doc);
|
||||
|
||||
/**
|
||||
* Notify the end of a Scribbly document
|
||||
* @param doc Document visited.
|
||||
*/
|
||||
void onEndDocument(ScribblyDocument doc);
|
||||
|
||||
/**
|
||||
* Notify the beginning of a Scribbly command.
|
||||
* @param command Command visited.
|
||||
*/
|
||||
void onBeginCommand(ScribblyCommand command);
|
||||
|
||||
/**
|
||||
* Notify the end of a Scribbly command.
|
||||
* @param command Command visited.
|
||||
*/
|
||||
void onEndCommand(ScribblyCommand command);
|
||||
|
||||
/**
|
||||
* Notify a text leaf of a Scribbly document.
|
||||
* @param text Leaf visited.
|
||||
*/
|
||||
void onText(ScribblyText text);
|
||||
}
|
Loading…
Reference in New Issue