/******************************************************************************* * This file is part of Goko. * * Goko is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Goko is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Goko. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package org.goko.core.gcode.rs274ngcv3.parser; import java.io.InputStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.goko.core.common.exception.GkException; import org.goko.core.common.exception.GkFunctionalException; import org.goko.core.common.i18n.MessageResource; import org.goko.core.common.utils.Location; import org.goko.core.gcode.element.validation.IValidationElement.ValidationSeverity; import org.goko.core.gcode.element.validation.IValidationTarget; import org.goko.core.gcode.element.validation.ValidationElement; /** * GCode file tokenizer * @author PsyKo * */ public class GCodeLexer { /** Multi line comment pattern */ private Pattern multilineCommentPattern; /** Simple comment pattern*/ private Pattern simpleCommentPattern; /** Line number pattern */ private Pattern lineNumberPattern; /** Word pattern*/ private Pattern wordPattern; /** White space detection pattern at the beginning*/ private Pattern spacePattern; /** Percent sign detection pattern at the beginning*/ private Pattern percentPattern; /** * Constructor */ public GCodeLexer() { multilineCommentPattern = Pattern.compile(GCodeTokenType.MULTILINE_COMMENT.getPattern(), Pattern.MULTILINE | Pattern.DOTALL); simpleCommentPattern = Pattern.compile(GCodeTokenType.SIMPLE_COMMENT.getPattern()); lineNumberPattern = Pattern.compile(GCodeTokenType.LINE_NUMBER.getPattern()); wordPattern = Pattern.compile(GCodeTokenType.WORD.getPattern()); spacePattern = Pattern.compile("^[ ]+"); percentPattern = Pattern.compile(GCodeTokenType.PERCENT.getPattern()); } /** * Create a list of token from a String * @param stringCommand the string to extract tokens from * @return a list of {@link GCodeToken} * @throws GkException GkException */ public List<GCodeToken> tokenize(String stringCommand, IValidationTarget validationTarget, int lineNumber) throws GkException{ List<GCodeToken> lstTokens = new ArrayList<GCodeToken>(); return createTokens(stringCommand, lstTokens, validationTarget, lineNumber, 0); } /** * Create a list of token from an InputStream * @param inStream the input stream * @return a list of {@link GCodeToken} * @throws GkException GkException */ public List<List<GCodeToken>> tokenize(InputStream inStream, IValidationTarget validationTarget) throws GkException{ Scanner scanner = new Scanner(inStream); List<List<GCodeToken>> lstFileTokens = new ArrayList<List<GCodeToken>>(); String line = null; List<GCodeToken> tokens = null; int lineNumber = 0; while(scanner.hasNextLine()){ line = scanner.nextLine(); tokens = tokenize(line, validationTarget, lineNumber); lineNumber++; lstFileTokens.add(tokens); } scanner.close(); return lstFileTokens; } /** * Recursive method used to split the stringCommand into a list of tokens * @param stringCommand the string command * @param tokens the list of token * @throws GkException GkException */ protected List<GCodeToken> createTokens(String pStringCommand, List<GCodeToken> tokens, IValidationTarget validationTarget, int lineNumber, int columnNumber) throws GkException{ String stringCommand = pStringCommand; int totalColumn = columnNumber + StringUtils.length(stringCommand); if(StringUtils.isBlank(stringCommand)){ return tokens; } Matcher spaceMatcher = spacePattern.matcher(stringCommand); if(spaceMatcher.find()){ String remainingString = spaceMatcher.replaceFirst(StringUtils.EMPTY); return createTokens(remainingString,tokens, validationTarget, lineNumber, totalColumn - StringUtils.length(remainingString)); } Matcher multilineCommentMatcher = multilineCommentPattern.matcher(stringCommand); if(multilineCommentMatcher.find()){ String remainingString = extractToken(multilineCommentMatcher, tokens, GCodeTokenType.MULTILINE_COMMENT); return createTokens(remainingString,tokens, validationTarget, lineNumber, totalColumn - StringUtils.length(remainingString)); } Matcher simpleCommentMatcher = simpleCommentPattern.matcher(stringCommand); if(simpleCommentMatcher.find()){ String remainingString = extractToken(simpleCommentMatcher, tokens,GCodeTokenType.SIMPLE_COMMENT); return createTokens(remainingString,tokens, validationTarget, lineNumber, totalColumn - StringUtils.length(remainingString)); } // Remove all white spaces ( comments already removed ) while(StringUtils.startsWith(stringCommand, " ")){ stringCommand = stringCommand.replaceFirst("\\s", StringUtils.EMPTY); columnNumber += 1; } Matcher lineNumberMatcher = lineNumberPattern.matcher(stringCommand); if(lineNumberMatcher.find()){ String remainingString = extractToken(lineNumberMatcher, tokens, GCodeTokenType.LINE_NUMBER); return createTokens(remainingString,tokens, validationTarget, lineNumber, totalColumn - StringUtils.length(remainingString)); } Matcher wordMatcher = wordPattern.matcher(stringCommand); if(wordMatcher.find()){ String remainingString = extractToken(wordMatcher, tokens, GCodeTokenType.WORD); return createTokens(remainingString,tokens, validationTarget, lineNumber, totalColumn - StringUtils.length(remainingString)); } Matcher percentMatcher = percentPattern.matcher(stringCommand); if(percentMatcher.find()){ String remainingString = extractToken(percentMatcher, tokens, GCodeTokenType.PERCENT); return createTokens(remainingString,tokens, validationTarget, lineNumber, totalColumn - StringUtils.length(remainingString)); } if(validationTarget != null){ ValidationElement vElement = new ValidationElement(ValidationSeverity.ERROR, new Location(lineNumber, columnNumber), StringUtils.length(stringCommand), MessageFormat.format(MessageResource.getMessage("GCO-101"), stringCommand)); validationTarget.addValidationElement(vElement); return tokens; }else{ throw new GkFunctionalException("GCO-101",stringCommand); } } /** * Extract the first token from the given matcher * @param matcher the matcher * @param tokens the list of tokens * @param type the type of token to create * @return the remaining String after the token extraction */ protected String extractToken(Matcher matcher, List<GCodeToken> tokens, GCodeTokenType type){ tokens.add( new GCodeToken(type, matcher.group()) ); return matcher.replaceFirst(StringUtils.EMPTY); } }