297 lines
11 KiB
C#
297 lines
11 KiB
C#
#if UNITY_EDITOR
|
|
using System.Collections.Generic;
|
|
|
|
namespace GSpawn
|
|
{
|
|
public class IntPatternParser : Parser
|
|
{
|
|
private IntPatternRootStatement _rootStatement;
|
|
private IntPatternLexer _patternLexer;
|
|
|
|
private HashSet<string> _intSeqences = new HashSet<string>();
|
|
|
|
public IntPatternRootStatement rootStatement { get { return _rootStatement; } }
|
|
|
|
protected override void parseImpl(Lexer lexer)
|
|
{
|
|
_patternLexer = lexer as IntPatternLexer;
|
|
if (_patternLexer == null) return;
|
|
|
|
_intSeqences.Clear();
|
|
_rootStatement = new IntPatternRootStatement();
|
|
while (!reachedEnd)
|
|
{
|
|
var stmt = statement();
|
|
if (stmt != null) _rootStatement.statements.Add(stmt);
|
|
}
|
|
}
|
|
|
|
protected override void syncOnError()
|
|
{
|
|
// Skip the token that generated the error.
|
|
advance();
|
|
|
|
// Now keep skipping until we find a good place to sit
|
|
while (!reachedEnd)
|
|
{
|
|
if (peekPreviousId() == IntPatternLexer.SEMI_COLON) return;
|
|
|
|
switch (peekId())
|
|
{
|
|
case IntPatternLexer.ADD:
|
|
|
|
return;
|
|
}
|
|
|
|
advance();
|
|
}
|
|
}
|
|
|
|
private IntPatternStatement statement()
|
|
{
|
|
if (peekId() == IntPatternLexer.ADD) return addStatement();
|
|
if (peekId() == IntPatternLexer.INT_SEQ) return intSeqDeclaration();
|
|
else
|
|
{
|
|
reportErrorAndSync("Invalid statement.", peek().line);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private IntPatternAddStatement addStatement()
|
|
{
|
|
if (!matchExpect(IntPatternLexer.ADD, "Expected 'add'.")) return null;
|
|
if (!matchExpect(IntPatternLexer.LEFT_PAREN, "Expected '(' after 'add'.")) return null;
|
|
|
|
var addStmt = new IntPatternAddStatement();
|
|
while (peekId() != IntPatternLexer.RIGHT_PAREN && !reachedEnd)
|
|
{
|
|
var expr = term();
|
|
if (expr == null) return null;
|
|
|
|
if (peekId() != IntPatternLexer.RIGHT_PAREN)
|
|
{
|
|
if (!matchExpect(IntPatternLexer.COMMA, "Expected ','.")) return null;
|
|
}
|
|
|
|
addStmt.addExpr(expr);
|
|
}
|
|
|
|
if (!matchExpect(IntPatternLexer.RIGHT_PAREN, "'add' statement must close with ')'.")) return null;
|
|
if (!matchExpect(IntPatternLexer.SEMI_COLON, "Expected ';' after 'add' statement.")) return null;
|
|
|
|
return addStmt;
|
|
}
|
|
|
|
private IntPatternIntSeqDeclStatement intSeqDeclaration()
|
|
{
|
|
if (!matchExpect(IntPatternLexer.INT_SEQ, "Expected 'IntSeq'.")) return null;
|
|
if (!matchExpect(IntPatternLexer.IDENTIFIER, "Expected identifier name (i.e. name of int sequence being declared).")) return null;
|
|
|
|
string seqName = peekPrevious().lexeme;
|
|
if (_intSeqences.Contains(seqName))
|
|
{
|
|
reportErrorAndSync("Int sequence '" + seqName + "' already declared.", peekPrevious().line);
|
|
return null;
|
|
}
|
|
|
|
if (!matchExpect(IntPatternLexer.EQUALS, "Expected '=' after int sequence name.")) return null;
|
|
|
|
var initExpr = term();
|
|
if (initExpr == null) return null;
|
|
|
|
if (!matchExpect(IntPatternLexer.SEMI_COLON, "Expected ';' after int sequence declaration statement.")) return null;
|
|
|
|
_intSeqences.Add(seqName);
|
|
return new IntPatternIntSeqDeclStatement(seqName, initExpr);
|
|
}
|
|
|
|
private IntPatternExpression primary()
|
|
{
|
|
switch (peekId())
|
|
{
|
|
case IntPatternLexer.REPEAT:
|
|
|
|
return repeat();
|
|
|
|
case IntPatternLexer.ISO_TRIANGLE:
|
|
|
|
return isoTriangle();
|
|
|
|
case IntPatternLexer.STEPS:
|
|
|
|
return steps();
|
|
|
|
case IntPatternLexer.IDENTIFIER:
|
|
|
|
string seqName = peek().lexeme;
|
|
if (!_intSeqences.Contains(seqName))
|
|
{
|
|
reportErrorAndSync("Undeclared int sequence '" + seqName + "'.", peek().line);
|
|
return null;
|
|
}
|
|
|
|
advance();
|
|
return new IntPatternIntSeqExpression(seqName);
|
|
|
|
case IntPatternLexer.LEFT_CURLY:
|
|
|
|
return intLiteralSequence();
|
|
|
|
default:
|
|
|
|
return intLiteral();
|
|
}
|
|
}
|
|
|
|
private IntPatternRepeatExpression repeat()
|
|
{
|
|
if (!matchExpect(IntPatternLexer.REPEAT, "Expected 'repeat'.")) return null;
|
|
if (!matchExpect(IntPatternLexer.LEFT_PAREN, "Expected '(' after 'repeat'.")) return null;
|
|
|
|
var expr = term();
|
|
if (expr == null) return null;
|
|
|
|
if (!matchExpect(IntPatternLexer.COMMA, "Expected ',' after first repeat argument.")) return null;
|
|
|
|
if (!matchExpect(IntPatternLexer.INT_LITERAL, expectedPositiveIntLiteralMsg())) return null;
|
|
int repCount = int.Parse(peekPrevious().lexeme);
|
|
if (repCount == 0)
|
|
{
|
|
reportErrorAndSync("Second argument to 'repeat' (i.e. repeat count) must be > 0.", peekPrevious().line);
|
|
return null;
|
|
}
|
|
|
|
if (!matchExpect(IntPatternLexer.RIGHT_PAREN, "'repeat' expression must close with ')'.")) return null;
|
|
return new IntPatternRepeatExpression(repCount, expr);
|
|
}
|
|
|
|
private IntPatternIsoTriangleEpression isoTriangle()
|
|
{
|
|
if (!matchExpect(IntPatternLexer.ISO_TRIANGLE, "Expected 'isoTriangle'.")) return null;
|
|
if (!matchExpect(IntPatternLexer.LEFT_PAREN, "Expected '(' after 'isoTriangle'.")) return null;
|
|
|
|
var triHeightExpr = signedIntLiteral();
|
|
if (triHeightExpr == null) return null;
|
|
|
|
if (triHeightExpr.value > -2 && triHeightExpr.value < 2)
|
|
{
|
|
reportErrorAndSync("Triangle height must be >= 2 OR <= -2.", peekPrevious().line);
|
|
return null;
|
|
}
|
|
|
|
if (!matchExpect(IntPatternLexer.RIGHT_PAREN, "'isoTriangle' expression must close with ')'.")) return null;
|
|
return new IntPatternIsoTriangleEpression(triHeightExpr.value);
|
|
}
|
|
|
|
private IntPatternStepsExpression steps()
|
|
{
|
|
if (!matchExpect(IntPatternLexer.STEPS, "Expected 'steps'.")) return null;
|
|
if (!matchExpect(IntPatternLexer.LEFT_PAREN, "Expected '(' after 'steps'.")) return null;
|
|
|
|
var numStepsExpr = signedIntLiteral();
|
|
if (numStepsExpr == null) return null;
|
|
if (numStepsExpr.value < 1)
|
|
{
|
|
reportErrorAndSync("The number of steps must be >= 1.", peekPrevious().line);
|
|
return null;
|
|
}
|
|
if (!matchExpect(IntPatternLexer.COMMA, "Expected ',' after first step argument.")) return null;
|
|
|
|
var stepLengthExpr = signedIntLiteral();
|
|
if (stepLengthExpr == null) return null;
|
|
if (stepLengthExpr.value < 1)
|
|
{
|
|
reportErrorAndSync("The step length must be >= 1.", peekPrevious().line);
|
|
return null;
|
|
}
|
|
if (!matchExpect(IntPatternLexer.COMMA, "Expected ',' after second step argument.")) return null;
|
|
|
|
if (!checkExpectAny("Expected true/false for last argument in 'steps'.", IntPatternLexer.TRUE, IntPatternLexer.FALSE)) return null;
|
|
int heightSign = peekId() == IntPatternLexer.TRUE ? -1 : 1;
|
|
advance();
|
|
|
|
if (!matchExpect(IntPatternLexer.RIGHT_PAREN, "'steps' expression must close with ')'.")) return null;
|
|
return new IntPatternStepsExpression(numStepsExpr.value, stepLengthExpr.value, heightSign);
|
|
}
|
|
|
|
private IntPatternExpression term()
|
|
{
|
|
var expr = unary();
|
|
if (expr == null) return null;
|
|
|
|
while (matchAny(IntPatternLexer.PLUS, IntPatternLexer.MINUS))
|
|
{
|
|
int opId = peekPreviousId();
|
|
|
|
var right = unary();
|
|
if (right == null) return null;
|
|
|
|
expr = new IntPatternBinaryExpression(expr, opId, right);
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
private IntPatternExpression unary()
|
|
{
|
|
if (match(IntPatternLexer.MINUS))
|
|
{
|
|
var prevToken = peekPrevious();
|
|
var expr = primary();
|
|
if (expr == null) return null;
|
|
return new IntPatternUnaryExpression(prevToken.id, expr);
|
|
}
|
|
|
|
return primary();
|
|
}
|
|
|
|
private IntPatternIntLiteralSequenceExpression intLiteralSequence()
|
|
{
|
|
int seqLine = peek().line;
|
|
if (!matchExpect(IntPatternLexer.LEFT_CURLY, "Expected '{' at the beginning of the integer sequence.")) return null;
|
|
|
|
var seqExpr = new IntPatternIntLiteralSequenceExpression();
|
|
while (peekId() != IntPatternLexer.RIGHT_CURLY && !reachedEnd)
|
|
{
|
|
var intExpr = signedIntLiteral();
|
|
if (intExpr == null) return null;
|
|
seqExpr.addValue(intExpr.value);
|
|
|
|
if (peekId() != IntPatternLexer.RIGHT_CURLY)
|
|
{
|
|
if (!matchExpect(IntPatternLexer.COMMA, "Expected ','.")) return null;
|
|
}
|
|
}
|
|
|
|
if (seqExpr.numValues == 0)
|
|
{
|
|
reportErrorAndSync("Empty integer sequences are not allowed. An integer sequence must contain at least one value.", seqLine);
|
|
return null;
|
|
}
|
|
|
|
if (!matchExpect(IntPatternLexer.RIGHT_CURLY, "Expected '}' at the end of the integer sequence.")) return null;
|
|
return seqExpr;
|
|
}
|
|
|
|
private IntPatternIntLiteralExpression intLiteral()
|
|
{
|
|
if (!matchExpect(IntPatternLexer.INT_LITERAL, expectedIntLiteralMsg())) return null;
|
|
return new IntPatternIntLiteralExpression(int.Parse(peekPrevious().lexeme));
|
|
}
|
|
|
|
private IntPatternIntLiteralExpression signedIntLiteral()
|
|
{
|
|
int sign = 1;
|
|
if (peekId() == IntPatternLexer.MINUS)
|
|
{
|
|
sign = -1;
|
|
advance();
|
|
}
|
|
|
|
if (!matchExpect(IntPatternLexer.INT_LITERAL, expectedIntLiteralMsg())) return null;
|
|
return new IntPatternIntLiteralExpression(sign * int.Parse(peekPrevious().lexeme));
|
|
}
|
|
}
|
|
}
|
|
#endif |