/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kie.dmn.feel.lang.types; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.Token; import org.kie.dmn.feel.parser.feel11.FEEL_1_1Lexer; import org.kie.dmn.feel.lang.Scope; import org.kie.dmn.feel.lang.Symbol; import org.kie.dmn.feel.lang.Type; import org.kie.dmn.feel.util.EvalHelper; import org.kie.dmn.feel.util.TokenTree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.Map.Entry; public class ScopeImpl implements Scope { public static final Logger LOG = LoggerFactory.getLogger(ScopeImpl.class); private String name; private Scope parentScope; private Map<String, Symbol> symbols = new LinkedHashMap<>(); private Map<String, Scope> childScopes = new LinkedHashMap<>(); private TokenTree tokenTree; private Type type; public ScopeImpl() { } public ScopeImpl(String name, Scope parentScope) { this.name = name; this.parentScope = parentScope; if ( parentScope != null ) { parentScope.addChildScope( this ); } } public ScopeImpl(String name, Scope parentScope, Type type) { this(name, parentScope); this.type = type; } public String getName() { return name; } public Scope getParentScope() { return parentScope; } public boolean define(Symbol symbol) { if ( symbols.containsKey( symbol.getId() ) ) { // duplicate symbol definition return false; } symbols.put( EvalHelper.normalizeVariableName( symbol.getId() ), symbol ); if( tokenTree != null ) { // also load the symbol into the token tree tokenTree.addName( tokenize( symbol.getId() ) ); } return true; } public Symbol resolve(String id) { Symbol s = symbols.get( EvalHelper.normalizeVariableName( id ) ); if ( s == null && parentScope != null ) { return parentScope.resolve( id ); } return s; } public Symbol resolve(String[] qualifiedName) { Symbol root = symbols.get( EvalHelper.normalizeVariableName( qualifiedName[0] ) ); if ( root == null && parentScope != null ) { return parentScope.resolve( qualifiedName ); } else if( root != null ) { Symbol currentSymbol = root; for( int i = 1; i < qualifiedName.length && currentSymbol != null; i++ ) { currentSymbol = currentSymbol.getScope().resolve( EvalHelper.normalizeVariableName( qualifiedName[i] ) ); } return currentSymbol; } return null; } public void setName(String name) { this.name = name; } public void setParentScope(Scope parentScope) { this.parentScope = parentScope; } public void addChildScope( Scope scope ) { this.childScopes.put( scope.getName(), scope ); } public Map<String, Scope> getChildScopes() { return childScopes; } public void setChildScopes(Map<String, Scope> childScopes) { this.childScopes = childScopes; } @Override public Map<String, Symbol> getSymbols() { return symbols; } public void start( String token ) { LOG.trace("[{}]: start() {}", name, token); if( tokenTree == null ) { initializeTokenTree(); } this.tokenTree.start( token ); if( this.parentScope != null ) { this.parentScope.start( token ); } } public boolean followUp( String token, boolean isPredict ) { LOG.trace("[{}]: followUp() {}", name, token); // must call followup on parent scope boolean parent = this.parentScope != null ? this.parentScope.followUp( token, isPredict ) : false; return this.tokenTree.followUp( token, !isPredict ) || parent; } private void initializeTokenTree() { LOG.trace("[{}]: initializeTokenTree()"); tokenTree = new TokenTree(); for( String symbol : symbols.keySet() ) { List<String> tokens = tokenize( symbol ); tokenTree.addName( tokens ); } } private List<String> tokenize(String symbol) { ANTLRInputStream input = new ANTLRInputStream(symbol); FEEL_1_1Lexer lexer = new FEEL_1_1Lexer( input ); List<String> tokens = new ArrayList<>( ); for (Token token = lexer.nextToken(); token.getType() != Token.EOF; token = lexer.nextToken()) { tokens.add( token.getText() ); } return tokens; } @Override public String toString() { return "Scope{" + " name='" + name + '\'' + ", parentScope='" + ( parentScope != null ? parentScope.getName() : "<null>" ) + "' }"; } @Override public Type getType() { return this.type; } }