Skip to content

Commit

Permalink
Work on #30, Fix for #23
Browse files Browse the repository at this point in the history
  • Loading branch information
iccir committed Nov 9, 2014
1 parent 8a74252 commit 17fed11
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 47 deletions.
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CHANGELOG

- Update to Esprima 1.2.2 #28
- Traverser's skip array causing 6% slowdown #29
- Add ability for typealias / typedef #30
- Classes can be superclasses of each other #23

---

Expand Down
8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
"ojc": "./bin/ojc"
},
"version": "1.0.0",
"engines": {
"node": ">=0.8.0"
},
"engines": { "node": ">=0.10" },
"maintainers": [{
"name": "Ricci Adams",
"email": "[email protected]",
Expand All @@ -29,8 +27,8 @@
"cpu-profiler": "1.x"
},
"dependencies": {
"lodash": "*",
"estraverse": "1.5.x",
"lodash": "2.x",
"estraverse": "1.7.x",
"source-map": "0.1.x",
"node-getopt": "0.2.x",
"jshint": "2.x"
Expand Down
8 changes: 8 additions & 0 deletions src/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ Builder.prototype.build = function()
}
}

function handleAtTypedefDeclaration(node)
{
model.aliasType(node.from.name, node.to.name);
}

function handleEnumDeclaration(node)
{
var length = node.declarations ? node.declarations.length : 0;
Expand Down Expand Up @@ -328,6 +333,9 @@ Builder.prototype.build = function()
} else if (type === Syntax.OJAtDynamicDirective) {
handleAtDynamicDirective(node);

} else if (type === Syntax.OJAtTypedefDeclaration) {
handleAtTypedefDeclaration(node);

} else if (type === Syntax.OJMethodDefinition) {
handleMethodDefinition(node);

Expand Down
4 changes: 3 additions & 1 deletion src/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ OJError.SelfIsReserved = "OJSelfIsReservedError";
OJError.DollarOJIsReserved = "OJDollarOJIsReservedError";
OJError.ReservedMethodName = "OJReservedMethodNameError";
OJError.SqueezerReachedEndIndex = "OJSqueezerReachedEndIndexError";

OJError.TypeAlreadyExists = "OJTypeAlreadyExistsError";
OJError.CircularTypedefHierarchy = "OJCircularTypedefHierarchyError";
OJError.CircularClassHierarchy = "OJCircularClassHierarchyError";

var OJWarning = { };

Expand Down
28 changes: 27 additions & 1 deletion src/esprima.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,8 @@ parseStatement: true, parseSourceElement: true */
(id === '@options') ||
(id === '@const') ||
(id === '@squeeze') ||
(id === '@cast');
(id === '@cast') ||
(id === '@typedef');
}
//!oj: end changes

Expand Down Expand Up @@ -3285,6 +3286,8 @@ parseStatement: true, parseSourceElement: true */
return delegate.markEnd(oj_parseEnumStatement(), startToken);
case '@options':
return delegate.markEnd(oj_parseEnumStatement(), startToken);
case '@typedef':
return delegate.markEnd(oj_parseTypedefDefinition(), startToken);
default:
break;
}
Expand Down Expand Up @@ -3801,6 +3804,7 @@ parseStatement: true, parseSourceElement: true */
Syntax.OJMethodDeclaration = "OJMethodDeclaration";
Syntax.OJAtCastExpression = "OJAtCastExpression";
Syntax.OJTypeAnnotation = "OJTypeAnnotation";
Syntax.OJAtTypedefDeclaration = "OJTypedefDeclaration";

Messages.OJCannotNestImplementations = "OJ: Cannot nest @implementation blocks";
}
Expand Down Expand Up @@ -4619,6 +4623,28 @@ parseStatement: true, parseSourceElement: true */
return delegate.markEnd(result, startToken);
}

function oj_parseTypedefDefinition() {
var fromIdentifier, toIdentifier, result,
startToken;

startToken = lookahead;

expectKeyword('@typedef');

fromIdentifier = parseVariableIdentifier();
toIdentifier = parseVariableIdentifier();

consumeSemicolon();

result = {
type: Syntax.OJAtTypedefDeclaration,
from: fromIdentifier,
to: toIdentifier
};

return delegate.markEnd(result, startToken);
}

function oj_parseTypeAnnotation() {
var token, value, startToken;

Expand Down
1 change: 1 addition & 0 deletions src/generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,7 @@ Generator.prototype.generate = function()
type === Syntax.OJInstanceVariableDeclarations ||
type === Syntax.OJAtSynthesizeDirective ||
type === Syntax.OJAtDynamicDirective ||
type === Syntax.OJAtTypedefDeclaration ||
((type === Syntax.OJEnumDeclaration) && removeEnums) ||
((type === Syntax.OJConstDeclaration) && removeConsts) ||
((type === Syntax.OJTypeAnnotation) && removeTypes)
Expand Down
140 changes: 105 additions & 35 deletions src/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,19 @@ function OJModel()
this._maxSqueezerId = 0;
this._squeezerToMap = { };
this._squeezerFromMap = { };
this._enumNames = { };

this.types = { };
this.registerType( [
"Array",
"Boolean",
"Number",
"Object",
"String",
"Symbol"
]);

this.aliasType( "Boolean", [ "boolean", "BOOL", "Bool", "bool" ] );
this.aliasType( "Number", [ "number", "double", "float", "int", "char", "short", "long" ] );
}


Expand Down Expand Up @@ -133,7 +145,7 @@ OJModel.prototype.loadState = function(state)
var enums = this.enums;
var classes = this.classes;
var protocols = this.protocols;
var enumNames = this._enumNames;
var types = this.types;

if (state.squeezer) {
this._squeezerId = state.squeezer.id || 0;
Expand All @@ -143,10 +155,10 @@ OJModel.prototype.loadState = function(state)

_.each(state.enums, function(e) {
enums.push(new OJEnum(e.name, e.unsigned, e.values));
enumNames[e.name] = true;
});

_.extend(this.consts, state.consts);
_.extend(this.types, state.types);

_.each(state.classes, function(c) {
var cls = new OJClass();
Expand Down Expand Up @@ -175,6 +187,7 @@ OJModel.prototype.saveState = function()
consts: this.consts,
enums: this.enums,
selectors: this.selectors,
types: this.types,

classes: _.map(this.classes, function(c) {
return c.saveState();
Expand All @@ -191,9 +204,24 @@ OJModel.prototype.prepare = function()
{
var selectors = { };

_.each(this.classes, function(cls, name) {
var classes = this.classes;
_.each(classes, function(cls, name) {
var i, length;

// Check for circular hierarchy
var visited = [ name ];
var superclass = cls.superclassName ? classes[cls.superclassName] : null;

while (superclass) {
if (visited.indexOf(superclass.name) >= 0) {
Utils.throwError(OJError.CircularClassHierarchy, "Circular class hierarchy detected: '" + visited.join(",") + "'");
}

visited.push(superclass.name);

superclass = classes[superclass.superclassName];
}

cls.doAutomaticSynthesis();

methods = cls.getAllMethods();
Expand All @@ -216,10 +244,77 @@ OJModel.prototype.prepare = function()
selectors[baseObjectSelectors[i]] = true;
}

var newTypes = { }
var types = this.types;
_.each(types, function(value, key) {
if (!value || (key == value)) {
newTypes[key] = value;
return;
}

var visited = [ key ];
var result = key;

while (1) {
var newResult = types[result];
if (newResult == result) break;

result = newResult;
if (!result) break;

if (visited.indexOf(result) >= 0) {
Utils.throwError(OJError.CircularTypedefHierarchy, "Circular typedef hierarchy detected: '" + visited.join(",") + "'");
}

visited.push(result);
}

newTypes[key] = result;
});
this.types = newTypes;

this.selectors = selectors;
}


OJModel.prototype.registerType = function(typesToRegister)
{
if (!_.isArray(typesToRegister)) {
typesToRegister = [ typesToRegister ];
}

for (var i = 0, length = typesToRegister.length; i < length; i++) {
var type = typesToRegister[i];

var currentValue = this.types[type];
if (currentValue && (currentValue != type)) {
Utils.throwError(OJError.TypeAlreadyExists, "Cannot register type '" + type + "', already declared as type '" + currentValue + "'");
}

this.types[type] = type;
}
}


OJModel.prototype.aliasType = function(existing, newTypes)
{
if (!_.isArray(newTypes)) {
newTypes = [ newTypes ];
}

for (var i = 0, length = newTypes.length; i < length; i++) {
var type = newTypes[i];

var currentValue = this.types[type];
if (currentValue && (currentValue != existing)) {
Utils.throwError(OJError.TypeAlreadyExists, "Cannot alias type '" + type + "' to '" + existing + "', already registered as type '" + currentValue + "'");
}

this.types[type] = existing;
}
}


OJModel.prototype.addConst = function(name, value)
{
this.consts[name] = value;
Expand All @@ -229,7 +324,7 @@ OJModel.prototype.addConst = function(name, value)
OJModel.prototype.addEnum = function(e)
{
this.enums.push(e);
this._enumNames[e.name] = true;
this.aliasType("Number", e.name);
}


Expand All @@ -241,12 +336,15 @@ OJModel.prototype.addClass = function(cls)
if (existing) {
if (existing.forward && !cls.forward) {
this.classes[name] = cls;
this.registerType(cls.name);

} else if (!existing.forward && !cls.forward) {
Utils.throwError(OJError.DuplicateClassDefinition, "Duplicate declaration of class '" + name +"'");
}

} else {
this.classes[name] = cls;
this.registerType(cls.name);
}
}

Expand All @@ -265,41 +363,13 @@ OJModel.prototype.addProtocol = function(protocol)

OJModel.prototype.isNumericType = function(t)
{
if (!t) return false;

var words = t.split(/\s+/);

for (var i = 0, length = words.length; i < length; i++) {
var word = words[i];

if (word == "Number" ||
word == "number" ||
word == "float" ||
word == "double" ||
word == "int" ||
word == "char" ||
word == "short" ||
word == "long")
{
return true;
}

if (this._enumNames[word]) {
return true;
}
}

return false;
return this.types[t] == "Number";
}


OJModel.prototype.isBooleanType = function(t)
{
return t == "Boolean" ||
t == "boolean" ||
t == "BOOL" ||
t == "Bool" ||
t == "bool";
return this.types[t] == "Boolean";
}


Expand Down
28 changes: 28 additions & 0 deletions test/multi/TestErrors.oj
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,32 @@ function test() {

@end

// --------------------------------------------------------------------
// @name Circular Class 1
// @error-no-line OJCircularClassHierarchyError

@implementation CircularClassA : CircularClassB
@end


@implementation CircularClassB : CircularClassA
@end


// --------------------------------------------------------------------
// @name Circular Class 2
// @error-no-line OJCircularClassHierarchyError

@implementation CircularClassA : CircularClassB
@end

@implementation CircularClassB : CircularClassC
@end

@implementation CircularClassC : CircularClassD
@end

@implementation CircularClassD : CircularClassA
@end


Loading

0 comments on commit 17fed11

Please sign in to comment.