All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.jlangch.venice.impl.debug.breakpoint.BreakpointParser Maven / Gradle / Ivy

There is a newer version: 1.12.35
Show newest version
/*   __    __         _
 *   \ \  / /__ _ __ (_) ___ ___
 *    \ \/ / _ \ '_ \| |/ __/ _ \
 *     \  /  __/ | | | | (_|  __/
 *      \/ \___|_| |_|_|\___\___|
 *
 *
 * Copyright 2017-2024 Venice
 *
 * 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 com.github.jlangch.venice.impl.debug.breakpoint;

import static com.github.jlangch.venice.impl.debug.breakpoint.FunctionScope.FunctionCall;
import static com.github.jlangch.venice.impl.debug.breakpoint.FunctionScope.FunctionException;
import static com.github.jlangch.venice.impl.debug.breakpoint.FunctionScope.FunctionExit;
import static com.github.jlangch.venice.impl.util.CollectionUtil.drop;
import static com.github.jlangch.venice.impl.util.CollectionUtil.toList;
import static com.github.jlangch.venice.impl.util.StringUtil.trimToEmpty;
import static com.github.jlangch.venice.impl.util.StringUtil.trimToNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import com.github.jlangch.venice.ParseError;
import com.github.jlangch.venice.impl.specialforms.SpecialForms;
import com.github.jlangch.venice.impl.types.util.QualifiedName;
import com.github.jlangch.venice.impl.util.StringUtil;


/**
 * Breakpoint parser. Parses function and file/line breakpoints
 */
public class BreakpointParser {

    /**
     * Parse breakpoint
     *
     * 

Breakpoint variants: *

    *
  • filter
  • *
  • foo/count
  • *
  • foo/count filter map
  • *
  • foo/count > reduce
  • *
  • foo/count + reduce
  • *
  • () user/sum
  • *
  • (!) user/sum + *
  • *
* * Selector style function break points: *
* foo/*: break in any function defined in namespace foo *
* function1 > function2: break in function2 if function1 is its immediate caller *
* function1 + function2: break in function2 if function1 is in the caller hierarchy * * @param definition the breakpoint definition * @return the parsed breakpoints */ public static List parseBreakpoints(final String definition) { return parseBreakpoints(Arrays.asList(definition.trim().split(" +"))); } public static List parseBreakpoints(final List tokens) { if (tokens.isEmpty()) { return new ArrayList(); } // First token: optional scopes final String scopes = trimToEmpty(tokens.get(0)); final boolean hasScopes = isBreakpointScopes(scopes); final Set scopeSet = parseBreakpointScopes( hasScopes ? scopes : null); final List bpTokens = hasScopes ? drop(tokens,1) : tokens; final String selector = bpTokens.size() == 3 ? bpTokens.get(1) : ""; switch (selector) { case ">": return toList( validate( new BreakpointFn( QualifiedName.parse(bpTokens.get(2)), new Selector( scopeSet, new AncestorSelector( QualifiedName.parse(bpTokens.get(0)), AncestorType.Nearest))))); case "+": return toList( validate( new BreakpointFn( QualifiedName.parse(bpTokens.get(2)), new Selector( scopeSet, new AncestorSelector( QualifiedName.parse(bpTokens.get(0)), AncestorType.Any))))); default: return bpTokens .stream() .map(s -> StringUtil.trimToNull(s.replace(',', ' '))) .filter(s -> isBreakpointRefCandidate(s)) .map(s -> parseBreakpoint(s, scopeSet)) .filter(b -> b != null) .map(s -> validate(s)) .collect(Collectors.toList()); } } /** * Parse a breakpoint given by a reference. * * @param ref a breakpoint reference * @return A breakpoint or null if the passed reference * could not be parsed */ private static BreakpointFn parseBreakpoint( final String ref, final Set scopes ) { if (StringUtil.isBlank(ref)) { return null; } try { final String ref_ = ref.trim(); if (isBreakpointFn(ref_)) { // function breakpoint return new BreakpointFn( QualifiedName.parse(ref_), new Selector(scopes)); } else { return null; } } catch(RuntimeException ex) { return null; } } public static Set parseBreakpointScopes( final String scopes ) { return parseBreakpointScopes(scopes, new HashSet<>()); } public static Set parseBreakpointScopes( final String scopes, final Set defaultScopes ) { if (trimToNull(scopes) == null) { return defaultScopes; } else { final Set tset = Arrays.asList(FunctionScope.values()) .stream() .filter(t -> scopes.contains(t.symbol())) .collect(Collectors.toSet()); return tset.isEmpty() ? defaultScopes : tset; } } public static boolean isBreakpointScopes(final String scopes) { return scopes.matches(BREAKPOINT_SCOPE_REGEX); } private static boolean isBreakpointFn(final String ref) { return ref.equals("/") || ref.matches("([^0-9/][^/]*|[^0-9/][^/]*/[^/]+)"); } private static boolean isBreakpointRefCandidate(final String s) { return s != null && !isBreakpointScopes(s); } private static BreakpointFn validate(final BreakpointFn bp) { final String fnName = bp.getQualifiedFnName(); if (SpecialForms.isSpecialForm(fnName)) { if (!isSpecialFormSupportedForDebugging(fnName)) { throw new ParseError( String.format( "The special form '%s' is not supported for debugging! " + "Only the forms %s are supported yet.", fnName, SUPPORTED_SPECIAL_FORMS .stream() .sorted() .map(s -> "'" + s + "'") .collect(Collectors.joining(", ")))); } bp.getSelectors().forEach(s -> { if (s.hasScope(FunctionCall) || s.hasScope(FunctionExit) || s.hasScope(FunctionException) ) { throw new ParseError( String.format( "Breakpoints on special forms like '%s' do not " + "support level %s, %s, or %s!", fnName, FunctionCall.description(), FunctionExit.description(), FunctionException.description())); } }); } return bp; } private static boolean isSpecialFormSupportedForDebugging(final String name) { return SUPPORTED_SPECIAL_FORMS.contains(name); } public static Set SUPPORTED_SPECIAL_FORMS = new HashSet<>( Arrays.asList( "if", "let", "bindings", "loop", "try", "try-with", "catch", "finally")); private static final String BREAKPOINT_SCOPE_REGEX = "^[>(!)]+$"; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy