Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/java/com/scriptbasic/interfaces/LexicalElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,10 @@ public interface LexicalElement extends SourceLocationBound {
*/
Boolean isSymbol(String lexeme);

/**
* @return true if the lexical element is colon
*/
Boolean isStatementSeparator();

Boolean isLineTerminator();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@
* date June 8, 2012
*/
public interface NestedStructureHouseKeeper {

static public enum EndOfStatementResult {
/**
* End of statement was consumed. Other processors should not be called.
*/
CONSUMED,

/**
* End of statement was processed. Other processors should be notified.
*/
PROCESSED
}

static public interface EndOfStatementProcessor {

EndOfStatementResult consumeEndOfStatement() throws AnalysisException;

}

/**
* Push a nested structure object on the housekeeping stack.
Expand Down Expand Up @@ -61,4 +79,18 @@ <T extends NestedStructure> T pop(Class<T> expectedClass)
* @throws AnalysisException when there are some elements on the stack
*/
void checkFinalState() throws AnalysisException;

/**
* Checks that there are no extra characters when the line analyzer
* expects it has finished analyzing the statement. If there are
* some extra characters on the line then throws syntax error exception.
* Otherwise it simply steps the lexical analyzer iterator over the symbol.
*
* @throws AnalysisException when there are extra character on the actual line
*/
void consumeEndOfStatement() throws AnalysisException;

void pushEndOfStatementProcessor(EndOfStatementProcessor endOfStatementProcessor);

EndOfStatementProcessor popEndOfStatementProcessor();
}
8 changes: 8 additions & 0 deletions src/main/java/com/scriptbasic/interfaces/SyntaxAnalyzer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.scriptbasic.interfaces;

import com.scriptbasic.spi.Command;

/**
* A syntax analyzer analyzes a program source using the result of the lexical
Expand All @@ -17,4 +18,11 @@ public interface SyntaxAnalyzer {
*/
BuildableProgram analyze() throws AnalysisException;

/**
* Add command to the currently build program
*
* @param command Command to be added to the current program
*/
void addCommand(Command command);

}
6 changes: 6 additions & 0 deletions src/main/java/com/scriptbasic/lexer/BasicLexicalElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,10 @@ public Boolean isLineTerminator() {
&& CharUtils.isNewLine(getLexeme().codePointAt(0));
}

@Override
public Boolean isStatementSeparator() {
return getType() == TYPE_SYMBOL && getLexeme().length() == 1
&& getLexeme().codePointAt(0) == ':';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import com.scriptbasic.interfaces.*;
import com.scriptbasic.log.Logger;
import com.scriptbasic.log.LoggerFactory;
import com.scriptbasic.utility.SyntaxExceptionUtility;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public abstract class AbstractNestedStructureHouseKeeper implements NestedStructureHouseKeeper {
Expand All @@ -17,6 +20,7 @@ public <T> boolean match(final Class<T> expectedClass) {
private final Stack<Structure> stack = new Stack<>();
private final LexicalAnalyzer analyzer;
private boolean stackIsHealthy = true;
private final Stack<EndOfStatementProcessor> endOfStatementProcessors = new Stack<>();

protected AbstractNestedStructureHouseKeeper(final LexicalAnalyzer analyzer) {
this.analyzer = analyzer;
Expand Down Expand Up @@ -89,5 +93,47 @@ public void checkFinalState() throws AnalysisException {
if (stack.size() > 0) {
throw new BasicSyntaxException("There is at least one opened block on the stack. Block is not properly closed.");
}
if (endOfStatementProcessors.size() > 0) {
throw new BasicSyntaxException("There is at least one unfinished statement.");
}
}

@Override
public void consumeEndOfStatement() throws AnalysisException {

final var numOfProcessors = endOfStatementProcessors.size();
if(numOfProcessors>0) {
// Copy processors and revert order
// Note: Processors might be unregistered while iterating them
List<EndOfStatementProcessor> processors = new ArrayList<>(numOfProcessors);
final var iter = endOfStatementProcessors.listIterator(endOfStatementProcessors.size());
while(iter.hasPrevious()) {
processors.add(iter.previous());
}
// Run processors
for(final var processor: processors) {
final var result = processor.consumeEndOfStatement();
if(result==EndOfStatementResult.CONSUMED) {
return;
}
}
}

final var le = analyzer.get();
if (le != null && !(le.isLineTerminator() || le.isStatementSeparator())) {
SyntaxExceptionUtility.throwSyntaxException(
"There are extra characters following the expression.", le);
}
}

@Override
public void pushEndOfStatementProcessor(EndOfStatementProcessor endOfStatementProcessor)
{
endOfStatementProcessors.push(endOfStatementProcessor);
}

@Override
public EndOfStatementProcessor popEndOfStatementProcessor() {
return endOfStatementProcessors.pop();
}
}
19 changes: 18 additions & 1 deletion src/main/java/com/scriptbasic/syntax/BasicSyntaxAnalyzer.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.scriptbasic.syntax;

import java.util.ArrayList;
import java.util.List;

import com.scriptbasic.interfaces.*;
import com.scriptbasic.spi.Command;

public final class BasicSyntaxAnalyzer implements SyntaxAnalyzer {
private final LexicalAnalyzer lexicalAnalyzer;
private final CommandFactory commandFactory;
private final NestedStructureHouseKeeper nestedStructureHouseKeeper;
private LexicalElement lexicalElement;
private LexicalElement lexicalElement;
private List<Command> additionalCommands;

public BasicSyntaxAnalyzer(final LexicalAnalyzer lexicalAnalyzer, final CommandFactory commandFactory,
final NestedStructureHouseKeeper nestedStructureHouseKeeper) {
Expand Down Expand Up @@ -50,12 +55,24 @@ public BuildableProgram analyze() throws AnalysisException {
buildableProgram.addCommand(newCommand);
}
}
if(additionalCommands!=null) {
additionalCommands.forEach(buildableProgram::addCommand);
additionalCommands = null;
}
this.lexicalElement = lexicalAnalyzer.peek();
}
nestedStructureHouseKeeper.checkFinalState();
buildableProgram.postprocess();
return buildableProgram;
}

@Override
public void addCommand(final Command command) {
if(additionalCommands==null) {
additionalCommands = new ArrayList<>();
}
additionalCommands.add(command);
}

public static void consumeIgnoredLine(final LexicalAnalyzer lexicalAnalyzer, String lexString) throws AnalysisException {
while (!lexString.equals("\n")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.scriptbasic.spi.LeftValue;
import com.scriptbasic.spi.LeftValueList;
import com.scriptbasic.syntax.AbstractAnalyzer;
import com.scriptbasic.utility.SyntaxExceptionUtility;

public abstract class AbstractCommandAnalyzer extends AbstractAnalyzer<Command>
implements CommandAnalyzer {
Expand Down Expand Up @@ -93,20 +92,14 @@ protected boolean isKeyWord(final String keyword) throws AnalysisException {
}

/**
* Checks that there are no extra characters on a program line when the line
* analyzer thinks that it has finished analyzing the line. If there are
* Checks that there are no extra characters when the line analyzer
* expects it has finished analyzing the statement. If there are
* some extra characters on the line then throws syntax error exception.
* Otherwise it simply steps the lexical analyzer iterator over the EOL
* symbol.
* Otherwise it simply steps the lexical analyzer iterator over the symbol.
*
* @throws AnalysisException when there are extra character on the actual line
*/
protected void consumeEndOfLine() throws AnalysisException {
final var le = ctx.lexicalAnalyzer.get();
if (le != null && !le.isLineTerminator()) {
SyntaxExceptionUtility.throwSyntaxException(
"There are extra characters following the expression after the '"
+ getName() + "' keyword", le);
}
protected void consumeEndOfStatement() throws AnalysisException {
Comment thread
PetrPytelka marked this conversation as resolved.
ctx.nestedStructureHouseKeeper.consumeEndOfStatement();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public Command analyze() throws AnalysisException {
final var node = newNode();
final var list = analyzeSimpleLeftValueList();
node.setLeftValueList(list);
consumeEndOfLine();
consumeEndOfStatement();
return node;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ protected void registerAndSwapNode(final AbstractCommandIfElseKind node)
pushNode(node);
}

protected void registerAndPopNode(final AbstractCommandIfElseKind node)
protected AbstractCommandIfElseKind registerAndPopNode(final AbstractCommandIfElseKind node)
throws AnalysisException {
ctx.nestedStructureHouseKeeper.pop(AbstractCommandIfElseKind.class).setNext(node);
var command = ctx.nestedStructureHouseKeeper.pop(AbstractCommandIfElseKind.class);
command.setNext(node);
return command;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.scriptbasic.interfaces.AnalysisException;
import com.scriptbasic.interfaces.Expression;
import com.scriptbasic.interfaces.ScriptBasicKeyWords;
import com.scriptbasic.spi.Command;

/**
* @author Peter Verhas
Expand All @@ -16,22 +15,14 @@ public AbstractCommandAnalyzerIfKind(final Context ctx) {
super(ctx);
}

protected abstract Command createNode(Expression condition) throws AnalysisException;

/*
* (non-Javadoc)
*
* @see com.scriptbasic.interfaces.Analyzer#analyze()
/**
* Analyse expression and THEN keyword
* @return expression for IF statement
* @throws AnalysisException error when missing then keyword or failed to parse expression
*/
@Override
public Command analyze() throws AnalysisException {
return createNode(analizeLine());
}

protected Expression analizeLine() throws AnalysisException {
protected Expression analyzeCondition() throws AnalysisException {
final var condition = analyzeExpression();
assertKeyWord(ScriptBasicKeyWords.KEYWORD_THEN);
consumeEndOfLine();
return condition;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public Command analyze() throws AnalysisException {
ctx.lexicalAnalyzer.resetLine();
final var commandLet = new CommandLet();
commandLet.setExpression(ctx.expressionAnalyzer.analyze());
consumeEndOfLine();
consumeEndOfStatement();
return commandLet;
} else {
final var functionName = lv.getIdentifier();
Expand All @@ -47,7 +47,7 @@ public Command analyze() throws AnalysisException {
if (needClosingParenthesis) {
consumeClosingParenthesis(ctx.lexicalAnalyzer);
}
consumeEndOfLine();
consumeEndOfStatement();
return new CommandCall(functionCall);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public Command analyze() throws AnalysisException {
} else {
analyzeCaseConditions(node);
}
consumeEndOfLine();
consumeEndOfStatement();

var lastSelectPart = ctx.nestedStructureHouseKeeper.pop(AbstractCommandSelectPart.class);
CommandSelect commandSelect = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private void defineDSLRule() throws AnalysisException {
if (!functionNameLexicalElement.isIdentifier()) {
throw new BasicSyntaxException("there should be a function name after the keyword 'call' defining a sentenceó");
}
consumeEndOfLine();
consumeEndOfStatement();
final String[] syntaxElements = sentence.split("\\s+");
if (syntaxElements.length == 0) {
throw new BasicSyntaxException("sentence can not be empty");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.scriptbasic.syntax.commands;

import com.scriptbasic.context.Context;
import com.scriptbasic.executors.commands.AbstractCommandIfElseKind;
import com.scriptbasic.executors.commands.CommandElse;
import com.scriptbasic.interfaces.AnalysisException;
import com.scriptbasic.interfaces.BasicSyntaxException;
import com.scriptbasic.spi.Command;

public class CommandAnalyzerElse extends AbstractCommandAnalyzerIfElseKind {
Expand All @@ -14,8 +16,12 @@ public CommandAnalyzerElse(final Context ctx) {
@Override
public Command analyze() throws AnalysisException {
final var node = new CommandElse();
consumeEndOfLine();
registerAndSwapNode(node);
AbstractCommandIfElseKind prevNode = registerAndPopNode(node);
if(prevNode instanceof CommandElse) {
throw new BasicSyntaxException("Multiple 'else' statements", ctx.lexicalAnalyzer.peek());
}
pushNode(node);
consumeEndOfStatement();
return node;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.scriptbasic.context.Context;
import com.scriptbasic.executors.commands.CommandElseIf;
import com.scriptbasic.interfaces.AnalysisException;
import com.scriptbasic.interfaces.Expression;
import com.scriptbasic.spi.Command;

/**
Expand All @@ -16,10 +15,15 @@ public CommandAnalyzerElseIf(final Context ctx) {
super(ctx);
}

protected Command createNode(final Expression condition) throws AnalysisException {
@Override
public Command analyze() throws AnalysisException {

final var condition = analyzeCondition();

final var node = new CommandElseIf();
node.setCondition(condition);
registerAndSwapNode(node);
consumeEndOfStatement();
return node;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public Command analyze() throws AnalysisException {
throw new BasicSyntaxException(
"Select has to be terminated with End Select statement");
}
consumeEndOfLine();
consumeEndOfStatement();

var lastSelectPart = ctx.nestedStructureHouseKeeper.pop(AbstractCommandSelectPart.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public CommandAnalyzerEndIf(final Context ctx) {
@Override
public Command analyze() throws AnalysisException {
final var node = new CommandEndIf();
consumeEndOfLine();
consumeEndOfStatement();
registerAndPopNode(node);
return node;
}
Expand Down
Loading