foam-framework
Version:
MVC metaprogramming framework
251 lines (196 loc) • 8.13 kB
JavaScript
/**
* @license
* Copyright 2012 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
CLASS({
name: 'Protobuf',
package: 'foam.grammars',
properties: [
{
name: 'grammar',
factory: function() {
return SkipGrammar.create({
__proto__: grammar,
START: sym('proto'),
d: range('0', '9'),
w: alt(sym('d'), range('a', 'z'), range('A', 'Z'), "_"),
a: alt(range('a', 'z'), range('A', 'Z')),
proto: repeat(alt(
sym('message'),
sym('extend'),
sym('enum'),
sym('import'),
sym('package'),
sym('option'),
sym('syntax'), ';')),
syntax: seq("syntax", "=", sym('strLit'), ";"),
import: seq("import", sym('strLit'), ";"),
package: seq("package", sym('ident'), repeat(seq(".", sym("ident"))), ";"),
option: seq("option", sym('optionBody'), ";"),
optionBody: seq(sym('ident'), repeat(seq(".", sym('ident'))), "=", sym('constant')),
message: seq("message", sym('ident'), sym('messageBody')),
extend: seq("extend", sym('userType'), sym('messageBody')),
enum: seq("enum", sym('ident'), sym("openBrace"), repeat(alt(sym('option'), sym('enumField'), ";")), sym("closeBrace")),
enumField: seq(sym('ident'), "=", sym('sintLit'), ";"),
service: seq("service", sym('ident'), sym("openBrace"), repeat(seq(sym('option'), sym('rpc')), ";"), sym("closeBrace")),
rpc: seq("rpc", sym('ident'), "(", sym('userType'), ")", "returns", "(", sym('userType'), ")", ";"),
openBrace: literal("{"),
closeBrace: literal("}"),
messageBody: seq(
sym('openBrace'),
repeat(
alt(sym('field'), sym('enum'), sym('message'), sym('extend'), sym('extensions'), sym('group'), sym('option'), ';')
),
sym('closeBrace')),
group: seq(sym('modifier'), "group", sym('camelIdent'), "=", sym('intLit'), sym('messageBody')),
// tag number must be 2^28-1 or lower
field: seq(
sym('modifier'),
sym('type'),
sym('ident'),
"=",
sym('intLit'),
optional(seq("[", sym('fieldOption'), repeat(",", sym('fieldOption') ), "]")),
";"),
fieldOption: alt(sym('optionBody'), seq("default", "=", sym('constant'))),
extensions: seq("extensions", sym('intLit'), "to", alt(sym('intLit'), "max"), ";"),
modifier: alt("required", "optional", "repeated"),
type: alt(
"double", "float", "int32", "int64", "uint32", "uint64",
"sint32", "sint64", "fixed32", "fixed64", "sfixed32",
"sfixed64", "bool", "string", "bytes", sym('userType')),
// leading dot for identifiers means they're fully qualified
userType: noskip(plus(seq(optional("."), sym('ident')))),
constant: alt(sym('ident'), sym('sintLit'), sym('floatLit'), sym('strLit'), sym('boolLit')),
ident: seq(sym('a'), repeat(sym('w'))),
// according to parser.cc, group names must start with a capital letter as a
// hack for backwards-compatibility
camelIdent: seq(range('A', 'Z'), repeat(sym('w'))),
intLit: alt(sym('decInt'), sym('hexInt'), sym('octInt')),
sintLit: alt(
seq(optional(alt('+', '-')), sym('decInt')),
sym('intLit')),
decInt: plus(sym('d')),
hexInt: seq('/0', alt('x', 'X'), plus(alt(range('A','F'), range('a', 'f'), range('0', '9')))),
octInt: seq('/0', plus(range('0', '7'))),
floatLit:
seq(
seq(
sym('decInt'),
optional('.', sym('decInt'))),
optional(
seq(
alt('E', 'e'),
optional(alt('+', '-')),
sym('decInt')))),
boolLit: alt("true", "false"),
strLit: noskip(seq(sym('quote'), repeat(alt(sym('hexEscape'), sym('octEscape'), sym('charEscape'), sym('quoteEscape'), not(sym('quote'), anyChar))) ,sym('quote'))),
quote: alt('"', "'"),
hexEscape: seq('\\', alt('x', 'X'), repeat(alt(range('A','F'), range('a', 'f'), range('0', '9'), undefined, 1,2))),
octEscape: seq('\\0', repeat(range('0', '7'), undefined, 1, 3)),
charEscape: seq('\\', alt('a', 'b', 'f', 'n', 'r', 't', 'v','?')),
quoteEscape: seq('\\"'),
}, alt(repeat0(alt(' ', '\t', '\n', '\r')), // Skip whitespace and C++ style comments.
seq('//', repeat0(notChar('\n')))));
}
},
{
name: 'parser',
factory: function() {
var X = this.X;
return this.grammar.addActions({
package: function(a) {
this.currentPackage = a[1];
if ( a[2] && a[2].length ) {
this.currentPackage += a[2].map(function(a) { return a.join(''); }).join('')
}
},
quoteEscape: function(a) {
return ['"'];
},
enumField: function(a) {
return [a[0], a[2]];
},
enum: function(a) {
var constants = {};
var name = a[1];
var values = a[3];
for ( var i = 0 ; i < values.length ; i++ ) {
var value = values[i];
constants[value[0]] = value[1][1];
}
var m = Model.create({
name: name,
package: this.currentPackage,
constants: constants
});
return m;
},
userType: function(a) {
return a[0].join('');
},
field: function(a) {
if (a[0] === 'repeated') {
return ArrayProperty.create({
subType: a[1],
name: a[2],
prototag: a[4]
});
} else {
var prop = Property.create({
type: a[1],
name: a[2],
prototag: a[4],
required: a[0] === 'required'
});
// TODO: Hack for enums unti they're modelled.
var subtype = (this.ctx || GLOBAL)[prop.type];
if ( subtype && subtype.type === 'Enum' ) {
prop.outProtobuf = function(obj, out) {
if ( this.f(obj) === "" ) return;
outProtobufPrimitive('int32', this.prototag, this.f(obj), out);
};
}
return prop;
}
},
message: function(a) {
var properties = [];
var models = [];
for (var i = 0; i < a[2].length; i++) {
if ( ! a[2][i] ) continue;
if ( Property.isInstance(a[2][i]) ) {
properties.push(a[2][i]);
} else if ( Model.isInstance(a[2][i]) ) {
models.push(a[2][i]);
}
}
var args = {
name: a[1],
properties: properties,
models: models
};
if ( this.currentPackage ) args.package = this.currentPackage;
var model = Model.create(args);
return model;
},
messageBody: function(a) { return a[1]; },
ident: function(a) { return a[0] + a[1].join(''); },
decInt: function(a) { return parseInt(a.join('')); }
});
}
}
]
});