blob: 0a190c8d4722686441ba97432c2f412efb8faae5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 protos software gmbh (http://www.protos.de).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* CONTRIBUTORS:
* Henrik Rentz-Reichert (initial contribution)
*
*******************************************************************************/
package org.eclipse.etrice.dctools.fsm.ast
import java.util.ArrayList
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstArrayAccessNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstBracketNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstBracketNode.BracketType
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstFeatureCallNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstIdentifierBracketNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstIdentifierNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstMatchNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstOperationCallNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstOtherNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstPeriodNode
import org.eclipse.etrice.dctools.fsm.ast.nodes.DCAstWhitespaceNode
import org.eclipse.etrice.dctools.fsm.ast.tokens.DCBracketToken
import org.eclipse.etrice.dctools.fsm.ast.tokens.DCBracketToken.BracketKind
import org.eclipse.etrice.dctools.fsm.ast.tokens.DCTextToken
import org.eclipse.etrice.dctools.fsm.ast.tokens.DCToken.Kind
/**
*
* Grammar:
*
* PartialMatch: BracketExpression | FeatureCall
*
* BracketExpression: RoundBracketExpression | CurlyBracketExpression | SquareBracketExpression
* RoundBracketExpression: LEFT_ROUNDBRACKET Match RIGHT_ROUNDBRACKET
* CurlyBracketExpression: LEFT_CURLYBRACKET Match RIGHT_CORLYBRACKET
* SquareBracketExpression: LEFT_SQUAREBRACKET Match RIGHT_SQUAREBRACKET
*
* FeatureCall: Call Whitespace (PERIOD Whitespace Call Whitespace)*
* Call: ArrayAccess | OperationCall | IDENTIFIER
* ArrayAccess: IDENTIFIER Whitespace SquareBracketExpression
* OperationCall: IDENTIFIER Whitespace RoundBracketExpression
*
* Whitespace: WHITESPACE*
*
* Match: (PartialMatch* Other)*
*
* Other: *
*/
class DCParser {
val DCScanner scanner
String text
ArrayList<DCTextToken> tokens
int currentPosition = 0
new(DCLanguage language) {
scanner = new DCScanner(language)
}
def parse(String text) {
this.text = text
this.tokens = scanner.scan(text)
Match(null)
}
/*
* rules
*/
private def DCAstMatchNode Match(DCAstNode parent) {
val mNode = new DCAstMatchNode(parent)
var continue = true
while (continue) {
while (PartialMatch(mNode)!==null) {}
if (Other(mNode)===null) {
continue = false
}
}
return mNode
}
private def DCAstOtherNode Other(DCAstNode parent) {
var DCAstOtherNode lastNode = null
var token = read
if (token!==null) {
while (token!==null) {
if (stopOther(token)) {
unread
token = null
}
else {
// we consumed a token
lastNode = new DCAstOtherNode(parent, text.substring(token.begin, token.begin + token.length), token)
// do we have a partial match after that token?
val pNode = PartialMatch(null)
if (pNode!==null) {
unread(pNode)
token = null
}
else {
token = read
}
}
}
}
return lastNode
}
private def boolean stopOther(DCTextToken token) {
if (token.token instanceof DCBracketToken) {
return true
}
if (token.token.kind==Kind.IDENTIFIER) {
return true
}
return false
}
private def DCAstNode PartialMatch(DCAstNode parent) {
var node = ConcreteBracketExpression(parent) as DCAstNode
if (node===null) {
node = FeatureCall(parent)
}
return node
}
private def DCAstBracketNode ConcreteBracketExpression(DCAstNode parent) {
var node = RoundBracketExpression(parent)
if (node===null) {
node = CurlyBracketExpression(parent)
}
if (node===null) {
node = SquareBracketExpression(parent)
}
return node
}
private def DCAstBracketNode RoundBracketExpression(DCAstNode parent) {
return ConcreteBracketExpression(parent, BracketKind.LEFT_ROUND, BracketKind.RIGHT_ROUND, BracketType.ROUND)
}
private def DCAstBracketNode CurlyBracketExpression(DCAstNode parent) {
return ConcreteBracketExpression(parent, BracketKind.LEFT_CURLY, BracketKind.RIGHT_CURLY, BracketType.CURLY)
}
private def DCAstBracketNode SquareBracketExpression(DCAstNode parent) {
return ConcreteBracketExpression(parent, BracketKind.LEFT_SQUARE, BracketKind.RIGHT_SQUARE, BracketType.SQUARE)
}
private def DCAstBracketNode ConcreteBracketExpression(DCAstNode parent, BracketKind left, BracketKind right, BracketType type) {
var token = read
if (token!==null) {
if (token.token instanceof DCBracketToken) {
var b = token.token as DCBracketToken
if (b.bracketKind==left) {
val contents = Match(parent)
val leftPos = token.begin
token = read
if (token!==null) {
if (token.token instanceof DCBracketToken) {
val rightPos = token.begin
b = token.token as DCBracketToken
if (b.bracketKind==right) {
return new DCAstBracketNode(parent, type, contents, leftPos, rightPos)
}
}
unread // second token
}
else {
// we reached the end without a matching right bracket
return new DCAstBracketNode(parent, type, contents, leftPos)
}
unread(contents)
}
}
unread // first token
}
return null
}
private def DCAstFeatureCallNode FeatureCall(DCAstNode parent) {
val callNode = Call(parent)
if (callNode!==null) {
val fcNode = new DCAstFeatureCallNode(parent)
callNode.parent = fcNode
// optional whitespace
Whitespace(fcNode)
// (PERIOD Whitespace Call)*
var token = read
while (token!==null) {
if (token.token.kind==Kind.PERIOD) {
val pNode = new DCAstPeriodNode(null, token.begin)
val wsNode = Whitespace(null)
val cNode = Call(null)
val wsNode2 = Whitespace(null)
if (pNode!==null) {
pNode.parent = fcNode
if (wsNode!==null) {
wsNode.parent = fcNode
}
if (cNode!==null) {
cNode.parent = fcNode
}
if (wsNode2!==null) {
wsNode2.parent = fcNode
}
if (cNode!==null) {
// next token
token = read
}
else {
token = null
}
}
else {
unread(pNode)
unread(wsNode)
unread(pNode)
unread(wsNode2)
unread // the token itself
token = null
}
}
else {
unread // the token itself
token = null
}
}
return fcNode
}
unread(callNode)
return null
}
private def DCAstNode Call(DCAstNode parent) {
// try ArrayAccess
var node = BracketExpressionCall(parent, [SquareBracketExpression], [p, i, w, b| new DCAstArrayAccessNode(p, i, w, b)])
if (node===null) {
// try OperationCall
node = BracketExpressionCall(parent, [RoundBracketExpression], [p, i, w, b| new DCAstOperationCallNode(p, i, w, b)])
}
if (node!==null) {
return node
}
else {
// try IDENTIFIER
val token = read
if (token!==null) {
if (token.token.kind==Kind.IDENTIFIER) {
val id = text.substring(token.begin, token.begin + token.length)
return new DCAstIdentifierNode(parent, id, token)
}
unread
}
}
return null
}
private def DCAstIdentifierBracketNode BracketExpressionCall(
DCAstNode parent,
(DCAstNode)=>DCAstBracketNode bracketExpression,
(DCAstNode, DCAstIdentifierNode, DCAstWhitespaceNode, DCAstBracketNode)=>DCAstIdentifierBracketNode factory
) {
val token = read
if (token!==null) {
if (token.token.kind==Kind.IDENTIFIER) {
// first child is ID
val id = text.substring(token.begin, token.begin + token.length)
val idNode = new DCAstIdentifierNode(parent, id, token)
// optional whitespace
val wsNode = Whitespace(parent)
// second child is BracketExpression
val node = bracketExpression.apply(parent)
if (node!==null) {
// the node of the bracket expression call
val callNode = factory.apply(parent, idNode, wsNode, node)
return callNode
}
unread(idNode)
unread(wsNode)
}
else {
unread
}
}
return null
}
private def DCAstWhitespaceNode Whitespace(DCAstNode parent) {
var token = read
val begin = if (token!==null) token.begin else 0
var int end = 0
var count = 0
while (token!==null) {
if (token.token.kind==Kind.WHITESPACE) {
end = token.begin + token.length
count++
token = read
}
else {
token = null
unread
}
}
if (count>0) {
val ws = text.substring(begin, end)
return new DCAstWhitespaceNode(parent, count, begin, ws)
}
return null
}
/*
* helpers
*/
private def DCTextToken read() {
if (currentPosition<tokens.size) {
tokens.get(currentPosition++)
}
else {
null
}
}
private def void unread() {
unread(1)
}
private def void unread(int i) {
currentPosition = currentPosition - i
if (currentPosition<0) {
println("oops")
}
}
private def void unread(DCAstNode node) {
if (node!==null) {
node.parent = null
unread(node.totallyReadTokens)
}
}
}