Initialize fr.feufochmar.scribbly library.

This commit is contained in:
Feufochmar 2023-05-19 20:40:22 +02:00
commit ff69792292
11 changed files with 589 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target/

23
.project Normal file
View File

@ -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>

40
pom.xml Normal file
View File

@ -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>

View File

@ -0,0 +1,13 @@
lexer grammar ScribblyLexer;
ARROBE : '@' ;
LBLOCK : '[' ;
RBLOCK : ']' ;
LCURLY : '{' ;
RCURLY : '}' ;
COLON : ':' ;
WS: [ \t\n\r\f]+ -> skip ;
ID: [a-z]+ ;
STRING: (~('@' | '[' | ']' | '{' | '}'))+ ;

View File

@ -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)
;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}