|
@@ -35,7 +35,8 @@ export class IVProgParser {
|
|
|
|
|
|
constructor (input, lexerClass) {
|
|
constructor (input, lexerClass) {
|
|
this.lexerClass = lexerClass;
|
|
this.lexerClass = lexerClass;
|
|
- this.lexer = new lexerClass(new InputStream(input));
|
|
|
|
|
|
+ this.inputStream = new InputStream(input)
|
|
|
|
+ this.lexer = new lexerClass(this.inputStream);
|
|
this.lexer.recover = AstHelpers.recover.bind(this.lexer);
|
|
this.lexer.recover = AstHelpers.recover.bind(this.lexer);
|
|
this.tokenStream = new CommonTokenStream(this.lexer);
|
|
this.tokenStream = new CommonTokenStream(this.lexer);
|
|
this.tokenStream.fill();
|
|
this.tokenStream.fill();
|
|
@@ -233,10 +234,10 @@ export class IVProgParser {
|
|
this.definedFuncsNameList.push(id);
|
|
this.definedFuncsNameList.push(id);
|
|
}
|
|
}
|
|
|
|
|
|
- checkVariableDuplicate (variableID, variableIDToken) {
|
|
|
|
|
|
+ checkVariableDuplicate (variableID, sourceInfo) {
|
|
const index = this.getCurrentVariableStack().indexOf(variableID);
|
|
const index = this.getCurrentVariableStack().indexOf(variableID);
|
|
if(index !== -1) {
|
|
if(index !== -1) {
|
|
- throw SyntaxErrorFactory.duplicate_variable(variableIDToken);
|
|
|
|
|
|
+ throw SyntaxErrorFactory.duplicate_variable(sourceInfo);
|
|
}
|
|
}
|
|
this.getCurrentVariableStack().push(variableID);
|
|
this.getCurrentVariableStack().push(variableID);
|
|
}
|
|
}
|
|
@@ -286,49 +287,91 @@ export class IVProgParser {
|
|
let initial = null;
|
|
let initial = null;
|
|
let dim1 = null;
|
|
let dim1 = null;
|
|
let dim2 = null;
|
|
let dim2 = null;
|
|
- const idToken = this.getToken();
|
|
|
|
|
|
+ let dimensions = 0;
|
|
|
|
+ const sourceInfo = SourceInfo.createSourceInfo(this.getToken())
|
|
const idString = this.parseID();
|
|
const idString = this.parseID();
|
|
- this.checkVariableDuplicate(idString,idToken);
|
|
|
|
|
|
+ this.checkVariableDuplicate(idString, sourceInfo);
|
|
// Check for array or vector
|
|
// Check for array or vector
|
|
- // ID[int/IDi][int/IDj]
|
|
|
|
|
|
+ // ID[int/IDi?][int/IDj?]
|
|
if (this.checkOpenBrace(true)) {
|
|
if (this.checkOpenBrace(true)) {
|
|
- this.pos++;
|
|
|
|
|
|
+ this.pos += 1;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
dim1 = this.parseArrayDimension();
|
|
dim1 = this.parseArrayDimension();
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
this.checkCloseBrace();
|
|
this.checkCloseBrace();
|
|
- this.pos++;
|
|
|
|
|
|
+ this.pos += 1;
|
|
|
|
+ dimensions += 1
|
|
if(this.checkOpenBrace(true)) {
|
|
if(this.checkOpenBrace(true)) {
|
|
- this.pos++;
|
|
|
|
|
|
+ this.pos += 1;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
dim2 = this.parseArrayDimension();
|
|
dim2 = this.parseArrayDimension();
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
this.checkCloseBrace();
|
|
this.checkCloseBrace();
|
|
|
|
+ this.pos += 1;
|
|
|
|
+ dimensions += 1
|
|
|
|
+ }
|
|
|
|
+ return this.parseArrayDeclaration(typeString, isConst, idString, sourceInfo, dimensions, dim1, dim2);
|
|
|
|
+ } else {
|
|
|
|
+ const equalsToken = this.getToken();
|
|
|
|
+ if(isConst && equalsToken.type !== this.lexerClass.EQUAL ) {
|
|
|
|
+ throw SyntaxErrorFactory.const_not_init(sourceInfo);
|
|
|
|
+ }
|
|
|
|
+ if(equalsToken.type === this.lexerClass.EQUAL) {
|
|
|
|
+ this.pos++;
|
|
|
|
+ initial = this.parseExpressionOR();
|
|
|
|
+ }
|
|
|
|
+ let declaration = declaration = new Commands.Declaration(idString, typeString, initial, isConst);
|
|
|
|
+ declaration.sourceInfo = sourceInfo;
|
|
|
|
+ const commaToken = this.getToken();
|
|
|
|
+ if(commaToken.type === this.lexerClass.COMMA) {
|
|
this.pos++;
|
|
this.pos++;
|
|
|
|
+ this.consumeNewLines();
|
|
|
|
+ return [declaration]
|
|
|
|
+ .concat(this.parseDeclaration(typeString, isConst));
|
|
|
|
+ } else {
|
|
|
|
+ return [declaration]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ parseArrayDeclaration (typeString, isConst, idString, sourceInfo, dimensions, dim1, dim2) {
|
|
const equalsToken = this.getToken();
|
|
const equalsToken = this.getToken();
|
|
|
|
+ let n_lines = dim1;
|
|
|
|
+ let n_columns = dim2;
|
|
|
|
+ let initial = null;
|
|
if(isConst && equalsToken.type !== this.lexerClass.EQUAL ) {
|
|
if(isConst && equalsToken.type !== this.lexerClass.EQUAL ) {
|
|
- throw SyntaxErrorFactory.const_not_init(idToken);
|
|
|
|
|
|
+ throw SyntaxErrorFactory.const_not_init(sourceInfo);
|
|
}
|
|
}
|
|
if(equalsToken.type === this.lexerClass.EQUAL) {
|
|
if(equalsToken.type === this.lexerClass.EQUAL) {
|
|
this.pos++;
|
|
this.pos++;
|
|
- initial = this.parseExpressionOR();
|
|
|
|
|
|
+ initial = this.parseArrayLiteral(typeString);
|
|
}
|
|
}
|
|
- let declaration = null;
|
|
|
|
- let dimensions = 0;
|
|
|
|
- if (dim1 !== null) {
|
|
|
|
- dimensions++;
|
|
|
|
- if(dim2 !== null) {
|
|
|
|
- dimensions++;
|
|
|
|
|
|
+ if(dimensions === 1) {
|
|
|
|
+ if(initial == null && dim1 == null) {
|
|
|
|
+ // cannot infer dimension
|
|
|
|
+ throw new Error("cannot infer dimension 1");
|
|
}
|
|
}
|
|
- declaration = new Commands.ArrayDeclaration(idString,
|
|
|
|
- new CompoundType(typeString, dimensions), dim1, dim2, initial, isConst);
|
|
|
|
} else {
|
|
} else {
|
|
- declaration = new Commands.Declaration(idString, typeString, initial, isConst);
|
|
|
|
|
|
+ if(initial == null && (dim1 == null || dim2 == null)) {
|
|
|
|
+ // cannot infer dimension
|
|
|
|
+ throw new Error("cannot infer dimension 2");
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- declaration.sourceInfo = SourceInfo.createSourceInfo(idToken);
|
|
|
|
|
|
+
|
|
|
|
+ if(dim1 == null) {
|
|
|
|
+ n_lines = new Expressions.IntLiteral(toInt(initial.lines));
|
|
|
|
+ n_lines.sourceInfo = sourceInfo;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dimensions > 1) {
|
|
|
|
+ if(dim2 == null) {
|
|
|
|
+ n_columns = new Expressions.IntLiteral(toInt(initial.columns));
|
|
|
|
+ n_columns.sourceInfo = sourceInfo;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ const declaration = new Commands.ArrayDeclaration(idString,
|
|
|
|
+ new CompoundType(typeString, dimensions), n_lines, n_columns, initial, isConst);
|
|
|
|
+ declaration.sourceInfo = sourceInfo;
|
|
const commaToken = this.getToken();
|
|
const commaToken = this.getToken();
|
|
if(commaToken.type === this.lexerClass.COMMA) {
|
|
if(commaToken.type === this.lexerClass.COMMA) {
|
|
this.pos++;
|
|
this.pos++;
|
|
@@ -336,7 +379,7 @@ export class IVProgParser {
|
|
return [declaration]
|
|
return [declaration]
|
|
.concat(this.parseDeclaration(typeString, isConst));
|
|
.concat(this.parseDeclaration(typeString, isConst));
|
|
} else {
|
|
} else {
|
|
- return [declaration]
|
|
|
|
|
|
+ return [declaration]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -366,6 +409,8 @@ export class IVProgParser {
|
|
//parse as variable
|
|
//parse as variable
|
|
this.pos++;
|
|
this.pos++;
|
|
return this.parseVariable(dimToken);
|
|
return this.parseVariable(dimToken);
|
|
|
|
+ } else if(dimToken.type === this.lexerClass.CLOSE_BRACE) {
|
|
|
|
+ return null;
|
|
} else {
|
|
} else {
|
|
throw SyntaxErrorFactory.invalid_array_dimension(this.lexer.literalNames[this.lexerClass.RK_INTEGER], dimToken);
|
|
throw SyntaxErrorFactory.invalid_array_dimension(this.lexer.literalNames[this.lexerClass.RK_INTEGER], dimToken);
|
|
}
|
|
}
|
|
@@ -406,39 +451,85 @@ export class IVProgParser {
|
|
return exp;
|
|
return exp;
|
|
}
|
|
}
|
|
|
|
|
|
- parseArrayLiteral () {
|
|
|
|
|
|
+ parseArrayLiteral (typeString) {
|
|
this.checkOpenCurly();
|
|
this.checkOpenCurly();
|
|
const beginArray = this.getToken();
|
|
const beginArray = this.getToken();
|
|
if (this.parsingArrayDimension >= 2) {
|
|
if (this.parsingArrayDimension >= 2) {
|
|
throw SyntaxErrorFactory.token_missing_list(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
|
|
throw SyntaxErrorFactory.token_missing_list(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
|
|
}
|
|
}
|
|
- this.pos++;
|
|
|
|
- this.parsingArrayDimension++;
|
|
|
|
|
|
+ this.pos += 1;
|
|
|
|
+ this.parsingArrayDimension += 1;
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
- const data = this.parseExpressionList();
|
|
|
|
|
|
+ let data = null;
|
|
|
|
+ const maybeCurlyOpen = this.checkOpenCurly(true);
|
|
|
|
+ if(maybeCurlyOpen) {
|
|
|
|
+ // This is potentially a list of vectors
|
|
|
|
+ data = this.parseVectorList(typeString);
|
|
|
|
+ } else {
|
|
|
|
+ data = this.parseExpressionList();
|
|
|
|
+ }
|
|
this.consumeNewLines();
|
|
this.consumeNewLines();
|
|
this.checkCloseCurly()
|
|
this.checkCloseCurly()
|
|
const endArray = this.getToken();
|
|
const endArray = this.getToken();
|
|
- this.pos++;
|
|
|
|
- this.parsingArrayDimension--;
|
|
|
|
- if (this.parsingArrayDimension === 0) {
|
|
|
|
- // if (!data.isValid) {
|
|
|
|
- // // TODO: better error message
|
|
|
|
- // console.log('invalid array');
|
|
|
|
- // throw new Error(`Invalid array at line ${beginArray.line}`);
|
|
|
|
- // }
|
|
|
|
- }
|
|
|
|
|
|
+ this.pos += 1;
|
|
|
|
+ this.parsingArrayDimension -= 1;
|
|
const sourceInfo = SourceInfo.createSourceInfoFromList(beginArray, endArray);
|
|
const sourceInfo = SourceInfo.createSourceInfoFromList(beginArray, endArray);
|
|
let dataDim = 1;
|
|
let dataDim = 1;
|
|
if(data[0] instanceof Expressions.ArrayLiteral) {
|
|
if(data[0] instanceof Expressions.ArrayLiteral) {
|
|
- dataDim++;
|
|
|
|
|
|
+ dataDim += 1;
|
|
}
|
|
}
|
|
- const type = new CompoundType(Types.UNDEFINED, dataDim);
|
|
|
|
|
|
+ const type = new CompoundType(typeString, dataDim);
|
|
const exp = new Expressions.ArrayLiteral(type, data);
|
|
const exp = new Expressions.ArrayLiteral(type, data);
|
|
exp.sourceInfo = sourceInfo;
|
|
exp.sourceInfo = sourceInfo;
|
|
|
|
+ if(!exp.isValid) {
|
|
|
|
+ // invalid array
|
|
|
|
+ throw new Error("invalid array");
|
|
|
|
+ }
|
|
return exp;
|
|
return exp;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Returns a list of ArrayLiterals. Helper function for parsing matrices
|
|
|
|
+ */
|
|
|
|
+ parseVectorList (typeString) {
|
|
|
|
+ let list = [];
|
|
|
|
+ let lastSize = null;
|
|
|
|
+ while(true) {
|
|
|
|
+ this.checkOpenCurly();
|
|
|
|
+ const beginArray = this.getToken();
|
|
|
|
+ if (this.parsingArrayDimension >= 2) {
|
|
|
|
+ throw SyntaxErrorFactory.token_missing_list(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
|
|
|
|
+ }
|
|
|
|
+ this.pos += 1;
|
|
|
|
+ this.parsingArrayDimension += 1;
|
|
|
|
+ this.consumeNewLines();
|
|
|
|
+ const data = this.parseExpressionList();
|
|
|
|
+ this.consumeNewLines();
|
|
|
|
+ this.checkCloseCurly()
|
|
|
|
+ const endArray = this.getToken();
|
|
|
|
+ this.pos += 1;
|
|
|
|
+ this.parsingArrayDimension -= 1;
|
|
|
|
+ if(lastSize == null) {
|
|
|
|
+ lastSize = data.length;
|
|
|
|
+ } else if (lastSize !== data.length) {
|
|
|
|
+ const expString = this.inputStream.getText(beginArray.start, endArray.stop);
|
|
|
|
+ throw new Error("Invalid size: " + expString);
|
|
|
|
+ }
|
|
|
|
+ const sourceInfo = SourceInfo.createSourceInfoFromList(beginArray, endArray);
|
|
|
|
+ const type = new CompoundType(typeString, 1);
|
|
|
|
+ const exp = new Expressions.ArrayLiteral(type, data);
|
|
|
|
+ exp.sourceInfo = sourceInfo;
|
|
|
|
+ list.push(exp);
|
|
|
|
+ const commaToken = this.getToken();
|
|
|
|
+ if(commaToken.type !== this.lexerClass.COMMA) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ this.pos += 1;
|
|
|
|
+ this.consumeNewLines();
|
|
|
|
+ }
|
|
|
|
+ return list
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Returns an object {type: 'variable', value: value}.
|
|
* Returns an object {type: 'variable', value: value}.
|
|
* @returns object with fields type and value
|
|
* @returns object with fields type and value
|
|
@@ -1071,7 +1162,9 @@ export class IVProgParser {
|
|
this.pos++;
|
|
this.pos++;
|
|
return this.getBoolLiteral(token);
|
|
return this.getBoolLiteral(token);
|
|
case this.lexerClass.OPEN_CURLY:
|
|
case this.lexerClass.OPEN_CURLY:
|
|
- return this.parseArrayLiteral();
|
|
|
|
|
|
+ // No more annonymous array
|
|
|
|
+ // return this.parseArrayLiteral();
|
|
|
|
+ throw SyntaxErrorFactory.annonymous_array_literal(token);
|
|
case this.lexerClass.ID:
|
|
case this.lexerClass.ID:
|
|
case this.lexerClass.LIB_ID:
|
|
case this.lexerClass.LIB_ID:
|
|
return this.parseIDTerm();
|
|
return this.parseIDTerm();
|