Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonardoVal committed Jul 6, 2015
0 parents commit 68e11a8
Show file tree
Hide file tree
Showing 28 changed files with 3,595 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Auto detect text files and perform LF normalization
*.js text=on
*.js eol=lf

# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union

# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
bower_components/
docs/docker/
tools/
.svn
.git
npm-debug.log

11 changes: 11 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
bower_components/
docs/docker/
lib/
node_modules/
src/
tests/
tools/
Gruntfile.js
.svn
.git
npm-debug.log
80 changes: 80 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/** Gruntfile for [jsser.js](http://github.com/LeonardoVal/jsser.js).
*/
var sourceFiles = [ 'src/__prologue__.js',
'src/registry.js',
'src/serialization.js',
'src/materialization.js',
'src/constructions.js',
'src/wrapup.js',
// end
'src/__epilogue__.js'];

// Init config. ////////////////////////////////////////////////////////////////
module.exports = function(grunt) {
grunt.file.defaultEncoding = 'utf8';
// Init config. ////////////////////////////////////////////////////////////////
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jison: { ///////////////////////////////////////////////////////////////////////////////////
build: {
options: {
type: 'slr',
moduleType: 'js'
},
files: {
'src/parser.js': 'src/parser.jison'
}
}
},
concat_sourcemap: { ////////////////////////////////////////////////////////////////////////
build: {
src: sourceFiles,
dest: 'build/<%= pkg.name %>.js',
options: {
separator: '\n\n'
}
},
},
uglify: { //////////////////////////////////////////////////////////////////////////////////
build: {
src: 'build/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js',
options: {
banner: '//! <%= pkg.name %> <%= pkg.version %>\n',
report: 'min',
sourceMap: true,
sourceMapIn: 'build/<%= pkg.name %>.js.map',
sourceMapName: 'build/<%= pkg.name %>.min.js.map'
}
}
},
karma: { ///////////////////////////////////////////////////////////////////////////////////
options: {
configFile: 'test/karma.conf.js'
},
build: { browsers: ['PhantomJS'] },
},
docker: { //////////////////////////////////////////////////////////////////////////////////
build: {
src: ["src/**/*.js", "README.md", 'docs/*.md'],
dest: "docs/docker",
options: {
colourScheme: 'borland',
ignoreHidden: true,
exclude: 'src/__prologue__.js,src/__epilogue__.js'
}
}
}
});
// Load tasks. /////////////////////////////////////////////////////////////////////////////////////
grunt.loadNpmTasks('grunt-concat-sourcemap');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-docker');

// Register tasks. /////////////////////////////////////////////////////////////////////////////////
grunt.registerTask('compile', ['concat_sourcemap:build', 'uglify:build']);
grunt.registerTask('test', ['compile', 'karma:build']);
grunt.registerTask('build', ['test', 'docker:build']);
grunt.registerTask('default', ['build']);
};
10 changes: 10 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
The MIT License
===============

Source code for `sermat.js` is Copyright (C) 2015 [Leonardo Val](mailto:[email protected]).

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.**
175 changes: 175 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
Sermat.js
=========

Sermat is a serialization and data exchange format, similar to [JSON](http://json.org/). It is meant
to be used when dealing with heavily marshalled interfaces separating the application being
developed. Examples of this include the communtication between
[web workers](http://www.whatwg.org/specs/web-workers/current-work/) or those and the rendering
thread in browsers, or between [iframes](http://www.w3schools.com/html/html_iframe.asp).

[![Built with Grunt](https://cdn.gruntjs.com/builtwith.png)](http://gruntjs.com/)

[![NPM](https://nodei.co/npm/sermat.png)](https://www.npmjs.com/package/sermat)

## Design

Sermat goals are the following:

+ _Extend JSON_: All valid JSON strings must be also valid Sermat strings, although the viceversa
may not be true.

+ _Allow custom types_: Sermat must be able to deal with instances of types other than the basic
Javascript types (booleans, numbers, strings, arrays and object literals). Users must be able to
register types to be used (like `Date` or user defined classes), in order to serialize them
properly and obtain an equivalent instance after parsing.

+ _Handle structures with complicated references_: Users must be able to serialize (and afterwards
properly materialize) data structures that are not strictly trees; i.e. values may be referenced
more than once, even if that means a cycle of references.

### Minor changes

Sermat addresses some minor quirks of JSON. Firstly, serializing `undefined` values will raise an
error, unless explicitly permitted. In that case `undefined` will be transformed to `null` in a
consistent manner.

```javascript
JSON.stringify(undefined); // Results in undefined
Sermat.serialize(undefined); // Raises "Sermat.serialize: Cannot serialize undefined value!"
Sermat.serialize(undefined, Sermat.ALLOW_UNDEFINED); // Results in "null"

JSON.stringify({a:undefined}); // Results in "{}"
Sermat.serialize({a:undefined}, Sermat.ALLOW_UNDEFINED); // Results in "{a:null}"
JSON.stringify([undefined]); // Results in "[null]"
Sermat.serialize([undefined], Sermat.ALLOW_UNDEFINED); // Results in "[null]"
```

`Infinity` and `NaN` values are allowed, as well as comments, using the block comment syntax of
Javascript. Strings may have multiple lines between the double quotes. Also, some escape sequences
that are valid in Javascript but not in JSON are accepted by Sermat (e.g. `'\v'` or `'\xA9'`). Keys
in object literals don't have to be between double quotes if they comply with [Javascript's rules
for identifiers](http://www.w3schools.com/js/js_variables.asp) and if all characters are in the
range `[\x00-\x7F]` (dots and dashes are also allowed).

### Constructions

One of the more important aspects of Sermat are _constructions_: a way of customizing serialization
and materialization for particular types. The user may register custom methods for serializing and
materializing objects built with a given constructor (e.g. `Date`). These objects will be written
in text form in a similar way a function call is written in Javascript.

```javascript
Sermat.serialize(new Date()); // Results like this: 'Date(2015,6,5,6,33,47,123)'.
Sermat.serialize(/\d+/g); // Results in: 'RegExp("\\\\d+","g")'.
```

When parsed, the result will be a properly initialized instance of the corresponding type. Some
Javascript base types are implemented _out-of-the-box_. Implementations for custom types can be
defined with `Sermat.register` or adding a `__SERMAT__` member to the constructor. In the following
example the identifier is defined to be `mylib.Point2D`, instead of the default `Point2D`.
identifiers cannot have dots, it is necessary to write it between double quotes.

```javascript
function Point2D(x, y) {
this.x = +x;
this.y = +y;
}
Sermat.serialize(new Point2D(44, 173)); // Raises "Sermat.serialize: Cannot serialize value!"
Point2D.__SERMAT__ = {
identifier: "mylib.Point2D",
serializer: function serialize_Point2D(obj) {
return [obj.x, obj.y];
},
materializer: function materialize_Point2D(obj, args) {
return args && (new Point2D(+args[0], +args[1]));
}
};
Sermat.register(Point2D);
Sermat.serialize(new Point2D(44, 173)); // Results in: 'mylib.Point2D(44,173)'
```

### References

Sermat by default does not allow objects to be serialized more than once. Having the same object as
a component in more than one place causes an error. For example:

```javascript
var obj1 = {a: 7};
Sermat.serialize([obj1, obj1]); // Raises "Sermat.serialize: Repeated reference detected!"
```

This behaviour can be changed in two ways. The first one is simply to allow values to be serialized
more than once, like JSON does.

```javascript
JSON.stringify([obj1, obj1]); // Results in '[{"a":7},{"a":7}]'.
Sermat.serialize([obj1, obj1], Sermat.ALLOW_REPEATED); // Results in '[{a:7},{a:7}]'.
```

The second one is part of another important feature called _bindings_. This is a syntax that allows
to bind values to identifiers (starting with `$`) so they can be reused in another place. When
parsed the resulting data structure is an acyclic graph instead of a tree.

```javascript
Sermat.serialize([obj1, obj1], Sermat.ALLOW_BINDINGS); // Results in '$0=[$1={a:7},$1]'.
```

Circular references are not supported by only allowing bindings. To make this work, circular
references have to be allowed explicitly.

```javascript
obj1.b = obj1;
Sermat.serialize([obj1, obj1], Sermat.ALLOW_BINDINGS); // Raises "Sermat.serialize: Circular reference detected!"
Sermat.serialize([obj1, obj1], Sermat.ALLOW_CIRCULAR); // Results in '$0=[$1={a:7,b:$1},$1]'.
```

Circular reference support in constructions requires the materializer functions to follow this
protocol. If a new instance must be built the `obj` argument will be `null`, else it will have an
instance already built to initialize. If an empty instance must be built the `args` argument will be
`null`, else it will have the arguments for the objects constructor (either with or without `new`).

When a construction is being parsed (after the identifier and before its arguments) the
corresponding materializer is called without arguments (`obj` and `args` both equal to `null`). If a
new _empty_ instance can be built it must be returned. The materializer will be called again later
(after the arguments have been parsed) with `obj` equal to this result, so the new instance can be
properly initialized. If the materializer cannot build an _empty_ instance, it must returns `null`.
This defers building the new object to after the arguments have been parsed, but then circular
references cannot be properly parsed.

This example show both cases. It uses the `sermat` function, which basically serializes a value and
then materializes it back, effectively cloning it.

```javascript
function Refs(refs) {
this.refs = Array.isArray(refs) ? refs : refs ? [refs] : [];
}
Refs.ALLOW_EMPTY_INSTANCES = false;
Sermat.register(Refs,
function serialize_Refs(obj) {
return obj.refs;
},
function materialize_Refs(obj, args) {
if (args === null) {
return Refs.ALLOW_EMPTY_INSTANCES ? (new Refs()) : null;
} else if (obj !== null) {
Refs.call(obj, args);
return obj;
} else {
return new Refs(args);
}
}
);
var refs1 = new Refs(obj1);
refs1.refs.push(refs1);
Sermat.sermat(refs1, Sermat.ALLOW_CIRCULAR); // Raises "Sermat.materialize: '$xx' is not bound at ...!".
Refs.ALLOW_EMPTY_INSTANCES = true;
Sermat.sermat(refs1, Sermat.ALLOW_CIRCULAR); // Returns a copy of refs1.
```

## License

Sermat is open source software, licenced under an [MIT license](LICENSE.md) (see LICENSE.md).

## Contact

Suggestions and comments are always welcome at [[email protected]](mailto:[email protected]).
6 changes: 6 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Sermat.js pending list
======================

+ Pretty printing capabilities, like the third parameter of `JSON.stringify`.

+ Add more Javascript base types to `CONSTRUCTIONS`.
Loading

0 comments on commit 68e11a8

Please sign in to comment.