Skip to content

Commit

Permalink
add exception
Browse files Browse the repository at this point in the history
  • Loading branch information
peze committed Nov 23, 2023
1 parent d793320 commit 1f717e9
Show file tree
Hide file tree
Showing 7 changed files with 1,032 additions and 25 deletions.
131 changes: 128 additions & 3 deletions lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class Parser extends BaseParser {
}

moduleBody() {
// moduleBody = "{" { const | type | model | api | function } "}"
// moduleBody = "{" { const | type | model | exception | api | function } "}"
const nodes = [];
while (this.look.tag) {
let node;
Expand All @@ -147,6 +147,8 @@ class Parser extends BaseParser {
node = this.typedef();
} else if (this.isWord(Tag.ID, 'model')) {
node = this.model();
} else if (this.isWord(Tag.ID, 'exception')) {
node = this.exception();
} else if (this.isWord(Tag.ID, 'enum')) {
node = this.enum();
} else if (this.isWord(Tag.ID, 'api')) {
Expand Down Expand Up @@ -587,6 +589,119 @@ class Parser extends BaseParser {
};
}

exception() {
// exception = "exception" exceptionName exceptionBody
const begin = this.getIndex();
this.matchWord(Tag.ID, 'exception');
const exceptionName = this.look;
this.match(Tag.ID);
let extendOn;
if (this.is(Tag.EXTENDS)) {
extendOn = this.extends();
}
// 可选的 =
if (this.look.tag === '=') {
this.move();
}

const body = this.exceptionBody();
let end = body.tokenRange[1];
if (this.is(';')) {
// 可选的 ;
end = this.getIndex();
this.move();
}

return {
type: 'exception',
exceptionName: exceptionName,
extendOn: extendOn,
exceptionBody: body,
tokenRange: [begin, end]
};
}

exceptionBody() {
// exceptionBody = "{" [ exceptionFields ] "}"
const begin = this.getIndex();
this.match('{');

const nodes = [];

if (this.is('}')) {
const end = this.getIndex();
this.move();
return {
type: 'exceptionBody',
nodes: nodes,
tokenRange: [begin, end]
};
}

var node = this.exceptionField();
nodes.push(node);

while (!this.is('}')) {
if (this.is(',')) {
this.move();

if (this.is('}')) {
// only one fields
break;
}

let node = this.exceptionField();
nodes.push(node);
} else {
this.error('expect ","');
}
}

const end = this.getIndex();
this.match('}');

return {
type: 'exceptionBody',
nodes: nodes,
tokenRange: [begin, end]
};
}


exceptionField() {
var required = true;
let fieldName = this.look;
const begin = this.getIndex();
if (this.is(Tag.ID)) {
this.move();
} else if (
this.isID()) {
fieldName.tag = Tag.ID;
this.move();
} else {
this.error(`only id is allowed`);
}

if (this.look.tag === '?') {
required = false;
this.move();
}

this.match(':');
const fieldValue = this.fieldValue();

const attrs = this.attrs();
const end = this.getIndex();
return {
type: 'exceptionField',
fieldName: fieldName,
required: required,
fieldValue: fieldValue,
attrs: attrs,
tokenRange: [begin, end]
};
}

fieldValue() {
// fieldValue = ( type | arrayType | modelBody | mapType )
// attrs = "(" attr { "," attr } ")"
Expand Down Expand Up @@ -1836,8 +1951,18 @@ class Parser extends BaseParser {
throwStmt() {
const begin = this.getIndex();
this.match(Tag.THROW);
let expr = this.object();
let end = expr.tokenRange[1];
let expr, end;
if(this.look.tag === '{'){
expr = this.object();
end = expr.tokenRange[1];
} else if(this.look.tag === Tag.NEW) {
expr = this.construct();
end = expr.object.tokenRange[1];
} else {
this.error('Unexpected expr: expect a exception or object.');
}


if (this.look.tag === ';') {
end = this.getIndex();
this.move();
Expand Down
72 changes: 56 additions & 16 deletions lib/semantic.js
Original file line number Diff line number Diff line change
Expand Up @@ -394,12 +394,6 @@ function isNeedToMap(expect, actual) {
return true;
}

function findProperty(model, propName) {
return model.modelBody.nodes.find((item) => {
return item.fieldName.lexeme === propName;
});
}

class TypeChecker {
constructor(source, filename, root, libraries, inner = false) {
this.source = source;
Expand Down Expand Up @@ -449,15 +443,40 @@ class TypeChecker {
throw new SyntaxError(message);
}

findProperty(model, propName) {
const find = model.modelBody.nodes.find((item) => {
return item.fieldName.lexeme === propName;
});
if(find || !model.extendOn) {
return find;
}
let extendModel;
if (model.extendOn.type === 'moduleModel') {
const [ main, ...path ] = model.extendOn.path;
const checker = this.dependencies.get(main.lexeme);
const typeName = path.map((item) => {
return item.lexeme;
}).join('.');
extendModel = checker.models.get(typeName);
} else if (model.extendOn.idType === 'builtin_model') {
extendModel = builtin.get(model.extendOn.lexeme);

} else {
extendModel = this.models.get(model.extendOn.lexeme);
}
return this.findProperty(extendModel, propName);
}

checkModels(ast) {
const models = ast.moduleBody.nodes.filter((item) => {
return item.type === 'model';
return item.type === 'model' || item.type === 'exception';
});
models.forEach((node) => {
const key = node.modelName.lexeme;
const name = node.type === 'model' ? node.modelName : node.exceptionName;
const key = name.lexeme;
// 重复定义检查
if (this.models.has(key)) {
this.error(`redefined model "${key}"`, node.modelName);
this.error(`redefined model or exception "${key}"`, name);
}
this.models.set(key, node);
});
Expand All @@ -467,7 +486,14 @@ class TypeChecker {
if(node.extendOn) {
this.checkType(node.extendOn);
}
this.visitModel(node);
if(node.type === 'model') {
this.visitModel(node);
} else if(node.type === 'exception') {
this.visitException(node);
} else {
this.error('unimplement');
}

});
}

Expand Down Expand Up @@ -1581,7 +1607,7 @@ class TypeChecker {
for (let i = 0; i < ast.propertyPath.length; i++) {
let prop = ast.propertyPath[i];
currentPath.push(prop.lexeme);
let find = findProperty(current, prop.lexeme);
let find = this.findProperty(current, prop.lexeme);
if (!find) {
this.error(`The model ${currentPath.join('.')} is undefined`, prop);
}
Expand Down Expand Up @@ -1801,7 +1827,7 @@ class TypeChecker {
for (let i = 0; i < ast.object.fields.length; i++) {
const field = ast.object.fields[i];
const name = field.fieldName.lexeme;
const modelField = findProperty(model, name);
const modelField = this.findProperty(model, name);
if (!modelField) {
this.error(`the property "${name}" is undefined in model "${aliasId}.${modelName}"`, field.fieldName);
}
Expand Down Expand Up @@ -2111,7 +2137,7 @@ class TypeChecker {
model = this.models.get(type.name);
}

const find = findProperty(model, propName);
const find = this.findProperty(model, propName);
if (!find) {
return;
}
Expand Down Expand Up @@ -2649,7 +2675,14 @@ class TypeChecker {
}

visitThrow(ast, env) {
this.visitObject(ast.expr, env);
if(ast.expr.type === 'object') {
this.visitObject(ast.expr, env);
} else if(ast.expr.type === 'construct_model') {
this.visitConstructModel(ast.expr, env);
} else {
this.error('unimplement', ast);
}

}

visitIf(ast, env) {
Expand Down Expand Up @@ -2687,7 +2720,7 @@ class TypeChecker {
}
}

flatModel(root, modelBody, modelName) {
flatModel(root, modelBody, modelName, extendOn) {
const keys = new Map();
for (var i = 0; i < modelBody.nodes.length; i++) {
const node = modelBody.nodes[i];
Expand Down Expand Up @@ -2740,6 +2773,7 @@ class TypeChecker {

this.models.set(modelName, {
type: 'model',
extendOn: extendOn,
modelName: {
tag: Tag.ID,
lexeme: modelName
Expand All @@ -2752,7 +2786,13 @@ class TypeChecker {
visitModel(ast) {
assert.equal(ast.type, 'model');
const modelName = ast.modelName.lexeme;
this.flatModel(ast, ast.modelBody, modelName);
this.flatModel(ast, ast.modelBody, modelName, ast.extendOn);
}

visitException(ast) {
assert.equal(ast.type, 'exception');
const exceptionName = ast.exceptionName.lexeme;
this.flatModel(ast, ast.exceptionBody, exceptionName, ast.extendOn);
}

visitEnum(ast) {
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/extend_model/Darafile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"libraries": {
"OSS": "./oss.dara"
}
}
40 changes: 40 additions & 0 deletions test/fixtures/extend_model/main.dara
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import OSS

model mainFile extends OSS.File {
size: number,
}

exception MainFileError extends OSS.File {
size: number,
}

exception ExtendFileError extends OSS.FileError {
size: number,
}

exception ExtendSubFileError extends OSS.SubFile.file {
size: number,
}

static function call(size: number): void {
var file = new mainFile{
name = 'name',
size = 100,
};
if(size > file.size) {
throw new MainFileError{
name = 'name',
size = 100,
}
} else if (size == file.size){
throw new ExtendFileError{
code = 'name',
size = 100,
}
} else {
throw new ExtendSubFileError{
name = 'name',
size = 100,
}
}
}
15 changes: 15 additions & 0 deletions test/fixtures/extend_model/oss.dara
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

model File = {
name: string,
};

exception FileError = {
code: string,
}

model SubFile = {
file: {
name: string,
}
}

Loading

0 comments on commit 1f717e9

Please sign in to comment.