Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.singingbush.sdl.Parser Maven / Gradle / Ivy
/*
* Simple Declarative Language (SDL) for Java
* Copyright 2005 Ikayzo, inc.
*
* This program is free software. You can distribute or modify it under the
* terms of the GNU Lesser General Public License version 2.1 as published by
* the Free Software Foundation.
*
* This program is distributed AS IS and WITHOUT WARRANTY. OF ANY KIND,
* INCLUDING MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, contact the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.singingbush.sdl;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.math.BigDecimal;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* The SDL parser.
*
* @author Daniel Leuck
*/
class Parser {
private static final String DATE_REGEX = "(\\d+\\/\\d+\\/\\d+)";
private static final String TIME_REGEX = "(\\d+:\\d+(:\\d+)?(.\\d+)?)(-\\w+)?";
public static final String DATETIME_REGEX = DATE_REGEX + " " + TIME_REGEX;
public static final String TIMESPAN_REGEX = "-?(\\d+d:)?(\\d+:\\d+:\\d+)(.\\d+)?";
private final BufferedReader reader;
private String line;
private List toks;
private StringBuilder sb;
private boolean startEscapedQuoteLine;
private int lineNumber=-1, lineStart = 0, pos=0, lineLength=0, tokenStart=0;
private boolean semicolonTerminated = false;
/**
* Create an SDL parser
*/
Parser(Reader reader) {
this.reader = (reader instanceof BufferedReader)
? ((BufferedReader)reader)
: new BufferedReader(reader);
}
/**
* Convenience for users wanting to parse SDL syntax that is held in a Java String
* @param sdlText a string of SDLang
* @since 1.4.0
*/
Parser(final String sdlText) {
this(new StringReader(sdlText));
//this(new InputStreamReader(new ByteArrayInputStream(sdlText.getBytes())));
}
/**
* Convenience for users wanting to parse SDL from a java.io.File
* @param file A UTF-8 encoded .sdl file
* @since 1.4.0
*/
Parser(final File file) throws FileNotFoundException, UnsupportedEncodingException {
this(new InputStreamReader(new FileInputStream(file), "UTF-8"));
}
/**
* @return A list of tags described by the input
* @throws IOException If a problem is encountered with the reader
* @throws SDLParseException If the document is malformed
*/
List parse() throws IOException,SDLParseException {
final List tags = new ArrayList<>();
List toks;
while((toks=getLineTokens()) != null) {
int size = toks.size();
if(toks.get(size-1).getType()==SdlType.START_BLOCK) {
Tag t = constructTag(toks.subList(0, size-1));
addChildren(t);
tags.add(t);
} else if(toks.get(0).getType()==SdlType.END_BLOCK){
parseException("No opening block ({) for close block (}).", toks.get(0).getLine(), toks.get(0).getPosition());
} else {
List tokens = new ArrayList<>(size);
for (final Token t : toks) {
tokens.add(t);
if(SdlType.SEMICOLON.equals(t.getType())) {
tags.add(constructTag(tokens));
tokens = new ArrayList<>(size);
}
}
if(!tokens.isEmpty()) {
tags.add(constructTag(tokens));
}
}
}
reader.close();
return tags;
}
private void addChildren(Tag parent) throws SDLParseException, IOException {
List toks;
while((toks=getLineTokens())!=null) {
int size = toks.size();
if(toks.get(0).getType()==SdlType.END_BLOCK) {
return;
} else if(toks.get(size-1).getType()==SdlType.START_BLOCK) {
Tag tag = constructTag(toks.subList(0, size-1));
addChildren(tag);
parent.addChild(tag);
} else {
parent.addChild(constructTag(toks));
}
}
// we have to use -2 for position rather than -1 for unknown because
// the parseException method adds 1 to line and position
parseException("No close block (}).", lineNumber, -2);
}
/**
* Construct a tag (but not its children) from a string of tokens
*
* @throws SDLParseException
*/
Tag constructTag(List toks) throws SDLParseException {
if(lineNumber ==141) {
int sdf=23;
}
if(toks.isEmpty())
// we have to use -2 for position rather than -1 for unknown because
// the parseException method adds 1 to line and position
parseException("Internal Error: Empty token list", lineNumber, -2);
Token t0 = toks.get(0);
if(t0.isLiteral()) {
toks.add(0, t0 = new Token("content", -1, -1));
} else if(!SdlType.IDENTIFIER.equals(t0.getType())) {
expectingButGot("IDENTIFIER", "" + t0.getType() + " (" + t0.getText() + ")",
t0.getLine(), t0.getPosition());
}
int size = toks.size();
Tag tag = null;
if(size == 1) {
tag = new Tag(t0.getText());
} else {
int valuesStartIndex = 1;
final Token t1 = toks.get(1);
if(SdlType.COLON.equals(t1.getType())) {
if(size==2 || !SdlType.IDENTIFIER.equals(toks.get(2).getType())) {
parseException("Colon (:) encountered in unexpected location.", t1.getLine(), t1.getPosition());
}
Token t2 = toks.get(2);
tag = new Tag(t0.getText(), t2.getText());
valuesStartIndex = 3;
} else {
tag = new Tag(t0.getText());
}
// read values
int i = addTagValues(tag, toks, valuesStartIndex);
// read attributes
if(i toks, int tpos) throws SDLParseException {
int size=toks.size(), i=tpos;
for(; i < size; i++) {
Token t = toks.get(i);
if(t.isLiteral()) {
// if(SdlType.DATETIME.equals(t.getType())) { // don't think I need this
// tag.addValue( new SdlValue<>(ZonedDateTime.class.cast(t.getObjectForLiteral().getValue()), SdlType.DATETIME) );
//// tag.addValue(LocalDateTime.class.cast(t.getObjectForLiteral()));
// }
tag.addValue(t.getObjectForLiteral());
} else if(SdlType.IDENTIFIER.equals(t.getType())) {
break;
// todo: CHECK THIS CODE - SOMETHING HERE IS WRONG/BROKEN
// if( (i+2) < size && SdlType.EQUALS.equals(toks.get(i+1).getType()) ) {
// tag.setAttribute(t.getText(), toks.get(i+2).getObjectForLiteral());
// i++;//i +=2;
// } else {
// break;
// }
//i++;//i +=2;
} else {
expectingButGot("LITERAL or IDENTIFIER", t.getType(), t.getLine(), t.getPosition());
}
}
return i;
}
/**
* Add attributes to the given tag
*/
private void addTagAttributes(final Tag tag, final List toks, final int tpos) throws SDLParseException {
int i = tpos;
final int size = toks.size();
while(i getLineTokens() throws SDLParseException, IOException {
// line = readLine();
if(semicolonTerminated) {
semicolonTerminated = false;
} else {
line = readLine();
}
if(line==null) {
return null;
}
toks = new ArrayList<>();
lineLength = line.length();
sb = null;
tokenStart=0;
for(;pos0 && tokString.charAt(0)=='"' && tokString.charAt(tokString.length()-1)!='"') {
parseException("String literal \"" + tokString + "\" not terminated by end quote.", lineNumber, line.length());
} else if(tokString.length()==1 && tokString.charAt(0)=='"') {
parseException("Orphan quote (unterminated string)", lineNumber, line.length());
}
}
}
private void handleEscapedDoubleQuotedString() throws SDLParseException, IOException {
if(pos==lineLength-1) {
line = readLine();
if(line==null) {
parseException("Escape at end of file.", lineNumber, pos);
}
lineLength = line.length();
pos=-1;
startEscapedQuoteLine=true;
} else {
// consume whitespace
int j=pos+1;
while(j. Strings must start and end with \"");
}
return literal.substring(1, literal.length()-1);
}
static String parseMultilineString(final String literal) {
if(literal.charAt(0) != '`' && literal.charAt(literal.length()-1) != '`') {
throw new IllegalArgumentException("Malformed string <" + literal + ">. String Literals must start and end with `");
}
return new String(literal.substring(1, literal.length() - 1).getBytes());
//text.replaceAll("\\", "\\\\");
}
static Character parseCharacter(String literal) {
if(literal.charAt(0)!='\'' || literal.charAt(literal.length()-1)!='\'') {
throw new IllegalArgumentException("Malformed character <" +
literal + ">. Character literals must start and end with single quotes.");
}
return Character.valueOf(literal.charAt(1));
}
static Number parseNumber(String literal) {
int textLength = literal.length();
boolean hasDot=false;
int tailStart=0;
for(int i=0; i < textLength; i++) {
char c=literal.charAt(i);
if("-0123456789".indexOf(c) == -1) {
if(c=='.') {
if(hasDot) {
throw new NumberFormatException("Encountered second decimal point.");
} else if(i == textLength-1) {
throw new NumberFormatException("Encountered decimal point at the end of the number.");
} else {
hasDot=true;
}
} else {
tailStart=i;
break;
}
} else {
tailStart=i+1;
}
}
final String number = literal.substring(0, tailStart);
final String tail = literal.substring(tailStart);
if(tail.length() == 0) {
if(hasDot) {
return new Double(number);
} else {
return new Integer(number);
}
}
if(tail.equalsIgnoreCase("BD")) {
return new BigDecimal(number);
} else if(tail.equalsIgnoreCase("L")) {
if(hasDot) {
throw new NumberFormatException("Long literal with decimal point");
}
return new Long(number);
} else if(tail.equalsIgnoreCase("F")) {
return new Float(number);
} else if(tail.equalsIgnoreCase("D")) {
return new Double(number);
}
throw new NumberFormatException("Could not parse number <" + literal + ">");
}
static LocalDate parseDate(final String literal) {
return LocalDate.parse(literal, DateTimeFormatter.ofPattern("y/M/d"));
}
static LocalTime parseTime(final String literal) {
final String[] split = literal.split(":|\\.|-");
//final StringBuilder pattern = new StringBuilder("H:m");
//LocalTime.of()
switch (split.length) {
case 2: // H:m
return LocalTime.parse(literal, DateTimeFormatter.ofPattern("H:m"));
//return LocalTime.of(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
case 3: // H:m:s
return LocalTime.parse(literal, DateTimeFormatter.ofPattern("H:m:s"));
//return LocalTime.of(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
case 4: // H:m:s.S
return LocalTime.parse(literal, DateTimeFormatter.ISO_LOCAL_TIME); //DateTimeFormatter.ofPattern("H:m:s.S")
case 5:
// can't handle time with a TimeZone but no Date
return null;
// return LocalTime.of(Integer.parseInt(split[0]),
// Integer.parseInt(split[1]),
// Integer.parseInt(split[2]),
// Integer.parseInt(split[3]));
// case 1:
// return LocalTime.parse(literal, DateTimeFormatter.ofPattern("H:m:s"));
// case 2:
// if(split[1].contains(".")) {
// return LocalTime.parse(literal, DateTimeFormatter.ofPattern("H:m:s.SSS"));
// } // else we are trying to parse "HH:mm:ss-Z" which is not possible
// break;
// case 3:
// // you cannot have ZonedTime without a date
// //return LocalTime.parse(literal, DateTimeFormatter.ofPattern("HH:mm:ss.SSS-Z"));
// break;
}
return null; // should there be an exception here???
}
static LocalDateTime parseLocalDateTime(final String literal) {
int[] intVals = {0,0,0,0,0,0,0};
final String[] strVals = literal.split("/| |:|\\.");
for (int i = 0; i < strVals.length; i++) {
intVals[i] = Integer.parseInt(strVals[i]);
}
return LocalDateTime.of(intVals[0], intVals[1], intVals[2], intVals[3], intVals[4], intVals[5], intVals[6]*1_000_000);
}
static ZonedDateTime parseZonedDateTime(final String literal) {
final TimeZone timeZone = literal.contains("-") ? TimeZone.getTimeZone(literal.substring(literal.indexOf("-") +1)) : TimeZone.getDefault();
int[] intVals = {0,0,0,0,0,0,0};
final String[] strVals = literal.contains("-") ? literal
.substring(0, literal.lastIndexOf("-"))
.split("/| |:|\\.") :
literal.split("/| |:|\\.");
for (int i = 0; i < strVals.length; i++) {
intVals[i] = Integer.parseInt(strVals[i]);
}
return ZonedDateTime.of(intVals[0], intVals[1], intVals[2], intVals[3], intVals[4], intVals[5], intVals[6]*1_000_000, timeZone.toZoneId());
}
// static Calendar parseDateTime(String literal) {
// int spaceIndex = literal.indexOf(' ');
// if(spaceIndex==-1) {
// return parseDate(literal);
// } else {
// Calendar dc = parseDate(literal.substring(0,spaceIndex));
// String timeString = literal.substring(spaceIndex+1);
//
// int dashIndex = timeString.indexOf('-');
// String tzString = null;
// if(dashIndex!=-1) {
// tzString=timeString.substring(dashIndex+1);
// timeString=timeString.substring(0, dashIndex);
// }
//
// String[] timeComps = timeString.split(":");
// if(timeComps.length<2 || timeComps.length>3)
// throw new IllegalArgumentException("Malformed time " +
// "component in date/time literal. Must use " +
// "hh:mm(:ss)(.xxx)");
//
// int hour = 0;
// int minute = 0;
// int second = 0;
// int millisecond = 0;
//
// // TODO - parse the time string, concatenate and return date/time
// try {
// hour=Integer.parseInt(timeComps[0]);
// minute=Integer.parseInt(timeComps[1]);
//
// if(timeComps.length==3) {
// String last = timeComps[2];
//
// int dotIndex = last.indexOf('.');
// if(dotIndex==-1) {
// second=Integer.parseInt(last);
// } else {
// second=Integer.parseInt(last.substring(0,dotIndex));
//
// String millis = last.substring(dotIndex+1);
// if(millis.length()==1)
// millis=millis+"00";
// else if(millis.length()==2)
// millis=millis+"0";
// millisecond=Integer.parseInt(millis);
// }
// }
// } catch(NumberFormatException nfe) {
// throw new IllegalArgumentException("Number format exception " +
// "in time portion of date/time literal \"" +
// nfe.getMessage() +"\"");
// }
//
// TimeZone tz = (tzString==null) ? TimeZone.getDefault() :
// TimeZone.getTimeZone(tzString);
//
// GregorianCalendar gc = new GregorianCalendar(tz);
// gc.set(dc.get(Calendar.YEAR), dc.get(Calendar.MONTH),
// dc.get(Calendar.DAY_OF_MONTH), hour, minute, second);
// gc.set(Calendar.MILLISECOND, millisecond);
// gc.getTime();
//
// return gc;
// }
// }
// static Calendar parseDate(String literal) {
// String[] comps = literal.split("/");
// if(comps.length!=3)
// throw new IllegalArgumentException("Malformed Date <" + literal + ">");
//
// try {
// return new GregorianCalendar(
// Integer.parseInt(comps[0]),
// Integer.parseInt(comps[1])-1,
// Integer.parseInt(comps[2])
// );
// } catch(final NumberFormatException e) {
// throw new IllegalArgumentException(String.format("Number format exception: \"%s\" for date literal <%s>", e.getMessage(), literal));
//
// }
// }
static byte[] parseBinary(String literal) {
final String stripped = literal.substring(1, literal.length()-1);
final StringBuilder sb = new StringBuilder();
final int btLength = stripped.length();
for(int i=0; i. Time spans must use the format " +
"(d:)hh:mm:ss(.xxx) Note: if the day component is " +
"included it must be suffixed with lower case \"d\"");
}
final boolean negate = literal.contains("-");
final List groups = Arrays.asList(literal.replace("-", "").split("d:|\\.")); // we should get 1 - 3 groups
int days=0; // optional
int hours=0; // mandatory
int minutes=0; // mandatory
int seconds=0; // mandatory
long milliseconds=0; // optional
for(final String group : groups) {
if(group.matches("\\d+:\\d+:\\d+")) {
final String[] segments = group.split(":");
hours = Integer.parseInt(segments[0]);
minutes = Integer.parseInt(segments[1]);
seconds = Integer.parseInt(segments[2]);
} else {
if(groups.indexOf(group) == 0) {
days = Integer.parseInt(group);
} else {
milliseconds = Long.parseLong(group);
}
}
}
final Duration duration = Duration.ofDays(days)
.plusHours(hours)
.plusMinutes(minutes)
.plusSeconds(seconds)
.plusMillis(milliseconds);
return negate ? duration.negated() : duration;
}
// static SDLTimeSpan parseTimeSpan(String literal) {
// int days=0; // optional
// int hours=0; // mandatory
// int minutes=0; // mandatory
// int seconds=0; // mandatory
// int milliseconds=0; // optional
//
// String[] segments = literal.split(":");
//
// if(segments.length<3 || segments.length>4)
// throw new IllegalArgumentException("Malformed time span <" +
// literal + ">. Time spans must use the format " +
// "(d:)hh:mm:ss(.xxx) Note: if the day component is " +
// "included it must be suffixed with lower case \"d\"");
//
// try {
// if(segments.length==4) {
// String dayString = segments[0];
// if(!dayString.endsWith("d")) {
// throw new IllegalArgumentException("The day component of a time span must end with a lower case d");
// }
//
// days = Integer.parseInt(dayString.substring(0, dayString.length()-1));
//
// hours = Integer.parseInt(segments[1]);
// minutes = Integer.parseInt(segments[2]);
//
// if(segments.length==4) {
// String last = segments[3];
// int dotIndex = last.indexOf(".");
//
// if(dotIndex==-1) {
// seconds = Integer.parseInt(last);
// } else {
// seconds =
// Integer.parseInt(
// last.substring(0, dotIndex));
//
// String millis = last.substring(dotIndex+1);
// if(millis.length()==1)
// millis=millis+"00";
// else if(millis.length()==2)
// millis=millis+"0";
//
// milliseconds =
// Integer.parseInt(millis);
// }
// }
//
// if(days<0) {
// hours=reverseIfPositive(hours);
// minutes=reverseIfPositive(minutes);
// seconds=reverseIfPositive(seconds);
// milliseconds=reverseIfPositive(milliseconds);
// }
// } else {
// hours=Integer.parseInt(segments[0]);
// minutes=Integer.parseInt(segments[1]);
//
// String last = segments[2];
// int dotIndex = last.indexOf(".");
//
// if(dotIndex==-1) {
// seconds = Integer.parseInt(last);
// } else {
// seconds = Integer.parseInt(last.substring(0, dotIndex));
//
// String millis = last.substring(dotIndex+1);
// if(millis.length()==1)
// millis=millis+"00";
// else if(millis.length()==2)
// millis=millis+"0";
// milliseconds = Integer.parseInt(millis);
// }
//
// if(hours<0) {
// minutes=reverseIfPositive(minutes);
// seconds=reverseIfPositive(seconds);
// milliseconds=reverseIfPositive(milliseconds);
// }
// }
// } catch(final NumberFormatException e) {
// throw new IllegalArgumentException(String.format("Number format in time span exception: \"%s\" for literal <%s>", e.getMessage(), literal));
// }
//
// return new SDLTimeSpan(days, hours, minutes, seconds, milliseconds);
// }
}