add owning method to statements

create common keyword statement class
This commit is contained in:
Dave Holoway
2020-06-25 11:43:53 +01:00
parent b0a2475696
commit f67c03bb34
20 changed files with 125 additions and 112 deletions

View File

@@ -62,8 +62,8 @@ const { TryStatement } = require("./statementtypes/TryStatement");
const { WhileStatement } = require("./statementtypes/WhileStatement");
/**
* @typedef {SourceMethod|SourceConstructor|SourceInitialiser} SourceMC
* @typedef {SourceType|SourceMC} Scope
* @typedef {import('./source-types').SourceMethodLike} SourceMethodLike
* @typedef {SourceType|SourceMethodLike} Scope
*/
@@ -394,7 +394,7 @@ function addLocals(tokens, mdecls, new_locals) {
/**
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
* @returns {Statement}
@@ -419,7 +419,7 @@ function statement(tokens, mdecls, method, imports, typemap) {
const locals = var_ident_list(modifiers, type, null, tokens, mdecls, method, imports, typemap)
addLocals(tokens, mdecls, locals);
semicolon(tokens);
return new LocalDeclStatement(locals);
return new LocalDeclStatement(method, locals);
}
switch(tokens.current.kind) {
@@ -442,9 +442,9 @@ function statement(tokens, mdecls, method, imports, typemap) {
const exp_or_vardecl = expression_or_var_decl(tokens, mdecls, method, imports, typemap);
if (Array.isArray(exp_or_vardecl)) {
addLocals(tokens, mdecls, exp_or_vardecl);
s = new LocalDeclStatement(exp_or_vardecl);
s = new LocalDeclStatement(method, exp_or_vardecl);
} else {
s = new ExpressionStatement(exp_or_vardecl);
s = new ExpressionStatement(method, exp_or_vardecl);
}
semicolon(tokens);
return s;
@@ -459,21 +459,20 @@ function statement(tokens, mdecls, method, imports, typemap) {
case 'open-bracket':
case 'new-operator':
const e = expression(tokens, mdecls, method, imports, typemap);
s = new ExpressionStatement(e);
s = new ExpressionStatement(method, e);
semicolon(tokens);
return s;
}
switch(tokens.current.value) {
case ';':
tokens.inc();
return new EmptyStatement();
return new EmptyStatement(method);
case '{':
return statementBlock(tokens, mdecls, method, imports, typemap);
case '}':
return new EmptyStatement();
return new EmptyStatement(method);
}
tokens.inc();
return new InvalidStatement(tokens.previous);
return new InvalidStatement(method, tokens.consume());
}
/**
@@ -875,12 +874,12 @@ function enumValueList(type, tokens, imports, typemap) {
/**
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
function statementBlock(tokens, mdecls, method, imports, typemap) {
const block = new Block(tokens.current);
const block = new Block(method, tokens.current);
tokens.expectValue('{');
mdecls.pushScope();
while (!tokens.isValue('}')) {
@@ -904,7 +903,7 @@ function semicolon(tokens) {
/**
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -912,8 +911,7 @@ function statementKeyword(tokens, mdecls, method, imports, typemap) {
let s;
switch (tokens.current.value) {
case 'if':
tokens.inc();
s = new IfStatement();
s = new IfStatement(method, tokens.consume());
s.test = bracketedTest(tokens, mdecls, method, imports, typemap);
s.statement = statement(tokens, mdecls, method, imports, typemap);
if (tokens.isValue('else')) {
@@ -921,74 +919,64 @@ function statementKeyword(tokens, mdecls, method, imports, typemap) {
}
break;
case 'while':
tokens.inc();
s = new WhileStatement();
s = new WhileStatement(method, tokens.consume());
s.test = bracketedTest(tokens, mdecls, method, imports, typemap);
s.statement = statement(tokens, mdecls, method, imports, typemap);
break;
case 'break':
s = new BreakStatement(tokens.consume());
s = new BreakStatement(method, tokens.consume());
s.target = tokens.getIfKind('ident');
semicolon(tokens);
break;
case 'continue':
s = new ContinueStatement(tokens.consume());
s = new ContinueStatement(method, tokens.consume());
s.target = tokens.getIfKind('ident');
semicolon(tokens);
break;
case 'switch':
tokens.inc();
s = new SwitchStatement();
s = new SwitchStatement(method, tokens.consume());
switchBlock(s, tokens, mdecls, method, imports, typemap);
break;
case 'do':
tokens.inc();
s = new DoStatement();
s = new DoStatement(method, tokens.consume());
s.block = statementBlock(tokens, mdecls, method, imports, typemap);
tokens.expectValue('while');
s.test = bracketedTest(tokens, mdecls, method, imports, typemap);
semicolon(tokens);
break;
case 'try':
tokens.inc();
s = new TryStatement();
s = new TryStatement(method, tokens.consume());
tryStatement(s, tokens, mdecls, method, imports, typemap);
break;
case 'return':
s = new ReturnStatement(tokens.current);
tokens.inc();
s = new ReturnStatement(method, tokens.consume());
s.expression = isExpressionStart(tokens.current) ? expression(tokens, mdecls, method, imports, typemap) : null;
semicolon(tokens);
break;
case 'throw':
tokens.inc();
s = new ThrowStatement();
s = new ThrowStatement(method, tokens.consume());
if (!tokens.isValue(';')) {
s.expression = isExpressionStart(tokens.current) ? expression(tokens, mdecls, method, imports, typemap) : null;
semicolon(tokens);
}
break;
case 'for':
tokens.inc();
s = new ForStatement();
s = new ForStatement(method, tokens.consume());
mdecls.pushScope();
forStatement(s, tokens, mdecls, method, imports, typemap);
mdecls.popScope();
break;
case 'synchronized':
tokens.inc();
s = new SynchronizedStatement();
s = new SynchronizedStatement(method, tokens.consume());
synchronizedStatement(s, tokens, mdecls, method, imports, typemap);
break;
case 'assert':
tokens.inc();
s = new AssertStatement();
s = new AssertStatement(method, tokens.consume());
assertStatement(s, tokens, mdecls, method, imports, typemap);
semicolon(tokens);
break;
default:
s = new InvalidStatement(tokens.current);
tokens.inc();
s = new InvalidStatement(method, tokens.consume());
break;
}
return s;
@@ -1012,7 +1000,7 @@ function bracketedTest(tokens, mdecls, scope, imports, typemap) {
* @param {TryStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1048,7 +1036,7 @@ function tryStatement(s, tokens, mdecls, method, imports, typemap) {
* @param {ForStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1084,7 +1072,7 @@ function forStatement(s, tokens, mdecls, method, imports, typemap) {
* @param {ForStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1101,7 +1089,7 @@ function enhancedFor(s, tokens, mdecls, method, imports, typemap) {
* @param {SynchronizedStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1116,7 +1104,7 @@ function synchronizedStatement(s, tokens, mdecls, method, imports, typemap) {
* @param {AssertStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1131,7 +1119,7 @@ function assertStatement(s, tokens, mdecls, method, imports, typemap) {
* @param {TryStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1184,7 +1172,7 @@ function catchFinallyBlocks(s, tokens, mdecls, method, imports, typemap) {
/**
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1201,7 +1189,7 @@ function catchType(tokens, mdecls, method, imports, typemap) {
* @param {SwitchStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1225,7 +1213,7 @@ function switchBlock(s, tokens, mdecls, method, imports, typemap) {
* @param {SwitchStatement} s
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1250,7 +1238,7 @@ function caseBlock(s, tokens, mdecls, method, imports, typemap) {
* @param {(ResolvedIdent|boolean)[]} cases
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/
@@ -1268,7 +1256,7 @@ function caseExpressionList(cases, tokens, mdecls, method, imports, typemap) {
/**
* @param {TokenList} tokens
* @param {MethodDeclarations} mdecls
* @param {SourceMC} method
* @param {SourceMethodLike} method
* @param {ResolvedImport[]} imports
* @param {Map<string,CEIType>} typemap
*/

View File

@@ -607,6 +607,10 @@ class FixedLengthArrayType extends SourceArrayType {
}
}
/**
* @typedef {SourceMethod|SourceConstructor|SourceInitialiser} SourceMethodLike
*/
exports.SourceType = SourceType;
exports.SourceTypeIdent = SourceTypeIdent;
exports.SourceField = SourceField;

View File

@@ -3,12 +3,12 @@
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const ParseProblem = require('../parsetypes/parse-problem');
const { isTypeAssignable } = require('../expression-resolver');
const { JavaType, PrimitiveType } = require('java-mti');
class AssertStatement extends Statement {
class AssertStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
expression = null;
/** @type {ResolvedIdent} */

View File

@@ -4,10 +4,10 @@
* @typedef {import('../body-types').Label} Label
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../source-types').SourceType} SourceType
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
const { Statement } = require("./Statement");
const ParseProblem = require('../parsetypes/parse-problem');
const { checkAssignment } = require('../expression-resolver');
class Block extends Statement {
/** @type {Statement[]} */
@@ -17,10 +17,11 @@ class Block extends Statement {
decls = null;
/**
* @param {SourceMethodLike} owner
* @param {Token} open
*/
constructor(open) {
super();
constructor(owner, open) {
super(owner);
this.open = open;
}

View File

@@ -1,28 +1,21 @@
/**
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const ParseProblem = require('../parsetypes/parse-problem');
class BreakStatement extends Statement {
class BreakStatement extends KeywordStatement {
/** @type {Token} */
target = null;
/**
* @param {Token} token
*/
constructor(token) {
super();
this.break_token = token;
}
/**
* @param {ValidateInfo} vi
*/
validate(vi) {
if (!vi.statementStack.find(s => /^(for|do|while|switch)$/.test(s))) {
vi.problems.push(ParseProblem.Error(this.break_token, `break can only be specified inside loop/switch statements`));
vi.problems.push(ParseProblem.Error(this.keyword, `break can only be specified inside loop/switch statements`));
}
}
}

View File

@@ -1,28 +1,21 @@
/**
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const ParseProblem = require('../parsetypes/parse-problem');
class ContinueStatement extends Statement {
class ContinueStatement extends KeywordStatement {
/** @type {Token} */
target = null;
/**
* @param {Token} token
*/
constructor(token) {
super();
this.continue_token = token;
}
/**
* @param {ValidateInfo} vi
*/
validate(vi) {
if (!vi.statementStack.find(s => /^(for|do|while)$/.test(s))) {
vi.problems.push(ParseProblem.Error(this.continue_token, `continue can only be specified inside loop statements`));
vi.problems.push(ParseProblem.Error(this.keyword, `continue can only be specified inside loop statements`));
}
}
}

View File

@@ -5,10 +5,10 @@
* @typedef {import('../expressiontypes/Expression').Expression} Expression
* @typedef {import('../statementtypes/Block').Block} Block
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const { checkBooleanBranchCondition } = require('../expression-resolver');
class DoStatement extends Statement {
class DoStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
test = null;
/** @type {Block} */

View File

@@ -3,6 +3,7 @@
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../expressiontypes/Expression').Expression} Expression
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
const { Statement } = require("./Statement");
const { BinaryOpExpression } = require('../expressiontypes/BinaryOpExpression');
@@ -13,10 +14,11 @@ const ParseProblem = require('../parsetypes/parse-problem');
class ExpressionStatement extends Statement {
/**
* @param {SourceMethodLike} owner
* @param {ResolvedIdent} expression
*/
constructor(expression) {
super();
constructor(owner, expression) {
super(owner);
this.expression = expression;
}

View File

@@ -1,13 +1,14 @@
/**
* @typedef {import('./Statement').Statement} Statement
* @typedef {import('../body-types').Local} Local
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../tokenizer').Token} Token
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const { checkNonVarDeclStatement } = require('../statement-validater');
class ForStatement extends Statement {
class ForStatement extends KeywordStatement {
/** @type {ResolvedIdent[] | Local[]} */
init = null;
/** @type {ResolvedIdent} */

View File

@@ -1,12 +1,13 @@
/**
* @typedef {import('./Statement').Statement} Statement
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const { checkBooleanBranchCondition } = require('../expression-resolver');
const { checkNonVarDeclStatement } = require('../statement-validater');
class IfStatement extends Statement {
class IfStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
test = null;
/** @type {Statement} */

View File

@@ -1,15 +1,16 @@
/**
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
const { Statement } = require("./Statement");
class InvalidStatement extends Statement {
/**
*
* @param {SourceMethodLike} owner
* @param {Token} token
*/
constructor(token) {
super();
constructor(owner, token) {
super(owner);
this.token = token;
}
}

View File

@@ -0,0 +1,22 @@
/**
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
const { Statement } = require("./Statement");
/**
* A statement that begins with a keyword (if, do, while, etc)
*/
class KeywordStatement extends Statement {
/**
* @param {SourceMethodLike} owner
* @param {Token} keyword
*/
constructor(owner, keyword) {
super(owner);
this.keyword = keyword;
}
}
exports.KeywordStatement = KeywordStatement;

View File

@@ -4,6 +4,7 @@
* @typedef {import('../body-types').Label} Label
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../source-types').SourceType} SourceType
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
const { Statement } = require("./Statement");
const ParseProblem = require('../parsetypes/parse-problem');
@@ -11,10 +12,11 @@ const { checkAssignment } = require('../expression-resolver');
class LocalDeclStatement extends Statement {
/**
* @param {SourceMethodLike} owner
* @param {Local[]} locals
*/
constructor(locals) {
super();
constructor(owner, locals) {
super(owner);
this.locals = locals;
}

View File

@@ -2,27 +2,20 @@
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../body-types').ResolvedValue} ResolvedValue
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
* @typedef {import('../tokenizer').Token} Token
*/
const { JavaType, PrimitiveType } = require('java-mti');
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const ParseProblem = require('../parsetypes/parse-problem');
const { isTypeAssignable } = require('../expression-resolver');
const { NumberLiteral } = require('../expressiontypes/literals/Number');
const { LambdaType, MultiValueType } = require('../anys');
class ReturnStatement extends Statement {
class ReturnStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
expression = null;
/**
* @param {Token} return_token
*/
constructor(return_token) {
super();
this.return_token = return_token;
}
/**
* @param {ValidateInfo} vi
*/
@@ -30,7 +23,7 @@ class ReturnStatement extends Statement {
const method_return_type = vi.method.returnType;
if (!this.expression) {
if (method_return_type !== PrimitiveType.map.V) {
vi.problems.push(ParseProblem.Error(this.return_token, `Method must return a value of type '${method_return_type.fullyDottedTypeName}'`));
vi.problems.push(ParseProblem.Error(this.keyword, `Method must return a value of type '${method_return_type.fullyDottedTypeName}'`));
}
return;
}

View File

@@ -1,7 +1,17 @@
/**
* @typedef {import('../source-types').SourceMethodLike} SourceMethodLike
*/
class Statement {
validate(vi) {}
/**
* @param {SourceMethodLike} owner
*/
constructor(owner) {
this.owner = owner;
}
validate(vi) {}
}
exports.Statement = Statement;

View File

@@ -1,15 +1,16 @@
/**
* @typedef {import('./Statement').Statement} Statement
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
* @typedef {import('../tokenizer').Token} Token
*/
const { JavaType, PrimitiveType } = require('java-mti');
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const ParseProblem = require('../parsetypes/parse-problem');
const { isTypeAssignable } = require('../expression-resolver');
const { NumberLiteral } = require('../expressiontypes/literals/Number');
class SwitchStatement extends Statement {
class SwitchStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
test = null;
/** @type {(ResolvedIdent|boolean)[]} */

View File

@@ -1,12 +1,13 @@
/**
* @typedef {import('./Statement').Statement} Statement
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
*/
const { CEIType } = require('java-mti');
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const ParseProblem = require('../parsetypes/parse-problem');
class SynchronizedStatement extends Statement {
class SynchronizedStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
expression = null;
/** @type {Statement} */

View File

@@ -3,11 +3,11 @@
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
*/
const { JavaType } = require('java-mti');
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const { isTypeAssignable } = require('../expression-resolver');
const ParseProblem = require('../parsetypes/parse-problem');
class ThrowStatement extends Statement {
class ThrowStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
expression = null;

View File

@@ -3,11 +3,10 @@
* @typedef {import('./Block').Block} Block
* @typedef {import('../body-types').Local} Local
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const { ResolvedIdent } = require('../body-types');
const ParseProblem = require('../parsetypes/parse-problem');
class TryStatement extends Statement {
class TryStatement extends KeywordStatement {
/** @type {(ResolvedIdent|Local[])[]} */
resources = [];
/** @type {Block} */

View File

@@ -1,11 +1,12 @@
/**
* @typedef {import('./Statement').Statement} Statement
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ValidateInfo} ValidateInfo
*/
const { Statement } = require("./Statement");
const { KeywordStatement } = require("./KeywordStatement");
const { checkBooleanBranchCondition } = require('../expression-resolver');
class WhileStatement extends Statement {
class WhileStatement extends KeywordStatement {
/** @type {ResolvedIdent} */
test = null;
/** @type {Statement} */