Revert "PEG Parser: under review at http://codereview.chromium.org/8399029"

This reverts commit e9f4f54f10af8a0b808b0fd4a1ea54384df0d7d9.

BUG=
TEST=

Review URL: http://codereview.chromium.org//8414016

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@870 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
sra@google.com 2011-10-28 05:34:41 +00:00
parent 5801bcfbb3
commit 9aacf5aa62
5 changed files with 0 additions and 2047 deletions

View file

@ -1,547 +0,0 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// IDL grammar variants.
final int WEBIDL_SYNTAX = 0;
final int WEBKIT_SYNTAX = 1;
final int FREMONTCUT_SYNTAX = 2;
/**
* IDLFile is the top-level node in each IDL file. It may contain modules or
* interfaces.
*/
class IDLFile extends IDLNode {
String filename;
List<IDLModule> modules;
List<IDLInterface> interfaces;
IDLFile(this.filename, this.modules, this.interfaces);
}
/**
* IDLModule has an id, and may contain interfaces, type defs andimplements
* statements.
*/
class IDLModule extends IDLNode {
String id;
List interfaces;
List typedefs;
List implementsStatements;
IDLModule(String this.id, IDLExtAttrs extAttrs, IDLAnnotations annotations,
List<IDLNode> elements) {
setExtAttrs(extAttrs);
this.annotations = annotations;
this.interfaces = elements.filter((e) => e is IDLInterface);
this.typedefs = elements.filter((e) => e is IDLTypeDef);
this.implementsStatements =
elements.filter((e) => e is IDLImplementsStatement);
}
toString() => '<IDLModule $id $extAttrs $annotations>';
}
class IDLNode {
IDLExtAttrs extAttrs;
IDLAnnotations annotations;
IDLNode();
setExtAttrs(IDLExtAttrs ea) {
assert(ea != null);
this.extAttrs = ea != null ? ea : new IDLExtAttrs();
}
}
class IDLType extends IDLNode {
String id;
IDLType parameter;
bool nullable = false;
IDLType(String this.id, [IDLType this.parameter, bool this.nullable = false]);
// TODO: Figure out why this constructor was failing in mysterious ways.
// IDLType.nullable(IDLType base) {
// return new IDLType(base.id, base.parameter, true);
// }
//String toString() => '<IDLType $nullable $id $parameter>';
String toString() {
String nullableTag = nullable ? '?' : '';
return '<IDLType $id${parameter == null ? '' : ' $parameter'}$nullableTag>';
}
}
class IDLTypeDef extends IDLNode {
String id;
IDLType type;
IDLTypeDef(String this.id, IDLType this.type);
toString() => '<IDLTypeDef $id $type>';
}
class IDLImplementsStatement extends IDLNode {
}
class IDLInterface extends IDLNode {
String id;
List parents;
List operations;
List attributes;
List constants;
List snippets;
bool isSupplemental;
bool isNoInterfaceObject;
bool isFcSuppressed;
IDLInterface(String this.id, IDLExtAttrs ea, IDLAnnotations ann,
List this.parents, List members) {
setExtAttrs(ea);
this.annotations = ann;
if (this.parents == null) this.parents = [];
operations = members.filter((e) => e is IDLOperation);
attributes = members.filter((e) => e is IDLAttribute);
constants = members.filter((e) => e is IDLConstant);
snippets = members.filter((e) => e is IDLSnippet);
isSupplemental = extAttrs.has('Supplemental');
isNoInterfaceObject = extAttrs.has('NoInterfaceObject');
isFcSuppressed = extAttrs.has('Suppressed');
}
toString() => '<IDLInterface $id $extAttrs $annotations>';
}
class IDLMember extends IDLNode {
String id;
IDLType type;
bool isFcSuppressed;
IDLMember(String this.id, IDLType this.type, IDLExtAttrs ea, IDLAnnotations ann) {
setExtAttrs(ea);
this.annotations = ann;
isFcSuppressed = extAttrs.has('Suppressed');
}
}
class IDLOperation extends IDLMember {
List arguments;
// Ignore all forms of raises for now.
List specials;
bool isStringifier;
IDLOperation(String id, IDLType type, IDLExtAttrs ea, IDLAnnotations ann,
List this.arguments, List this.specials, bool this.isStringifier)
: super(id, type, ea, ann) {
}
toString() => '<IDLOperation $type $id ${printList(arguments)}>';
}
class IDLAttribute extends IDLMember {
}
class IDLConstant extends IDLMember {
var value;
IDLConstant(String id, IDLType type, IDLExtAttrs ea, IDLAnnotations ann,
var this.value)
: super(id, type, ea, ann);
}
class IDLSnippet extends IDLMember {
String text;
IDLSnippet(IDLAnnotations ann, String this.text)
: super(null, null, new IDLExtAttrs(), ann);
}
/** Maps string to something. */
class IDLDictNode {
Map<String, Object> map;
IDLDictNode() {
map = new Map<String, Object>();
}
setMap(List associationList) {
if (associationList == null) return;
for (var element in associationList) {
var name = element[0];
var value = element[1];
map[name] = value;
}
}
bool has(String key) => map.containsKey(key);
formatMap() {
if (map.isEmpty())
return '';
StringBuffer sb = new StringBuffer();
map.forEach((k, v) {
sb.add(' $k');
if (v != null) {
sb.add('=$v');
}
});
return sb.toString();
}
}
class IDLExtAttrs extends IDLDictNode {
IDLExtAttrs([List attrs = const []]) {
setMap(attrs);
}
toString() => '<IDLExtAttrs${formatMap()}>';
}
class IDLArgument extends IDLNode {
String id;
IDLType type;
bool isOptional;
bool isIn;
bool hasElipsis;
IDLArgument(String this.id, IDLType this.type, IDLExtAttrs extAttrs,
bool this.isOptional, bool this.isIn, bool this.hasElipsis) {
setExtAttrs(extAttrs);
}
toString() => '<IDLArgument $id>';
}
class IDLAnnotations extends IDLDictNode {
IDLAnnotations(List annotations) {
for (var annotation in annotations) {
map[annotation.id] = annotation;
}
}
toString() => '<IDLAnnotations${formatMap()}>';
}
class IDLAnnotation extends IDLDictNode {
String id;
IDLAnnotation(String this.id, List args) {
setMap(args);
}
toString() => '<IDLAnnotation $id${formatMap()}>';
}
class IDLExtAttrFunctionValue extends IDLNode {
String name;
List arguments;
IDLExtAttrFunctionValue(String this.name, this.arguments);
toString() => '<IDLExtAttrFunctionValue $name(${arguments.length})>';
}
class IDLParentInterface extends IDLNode {}
////////////////////////////////////////////////////////////////////////////////
class IDLParser {
final int syntax;
Grammar grammar;
var axiom;
IDLParser([syntax=WEBIDL_SYNTAX]) : syntax = syntax {
grammar = new Grammar();
axiom = _makeParser();
}
_makeParser() {
Grammar g = grammar;
syntax_switch([WebIDL, WebKit, FremontCut]) {
assert(WebIDL != null && WebKit != null); // Not options, just want names.
if (syntax == WEBIDL_SYNTAX)
return WebIDL;
if (syntax == WEBKIT_SYNTAX)
return WebKit;
if (syntax == FREMONTCUT_SYNTAX)
return FremontCut == null ? WebIDL : FremontCut;
throw new Exception('unsupported IDL syntax $syntax');
}
var idStartCharSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_';
var idNextCharSet = idStartCharSet + '0123456789';
var hexCharSet = '0123456789ABCDEFabcdef';
var idStartChar = CHAR(idStartCharSet);
var idNextChar = CHAR(idNextCharSet);
var digit = CHAR('0123456789');
var Id = TEXT(LEX('an identifier',[idStartChar, MANY(idNextChar, min:0)]));
var IN = SKIP(LEX("'in'", ['in',NOT(idNextChar)]));
var BooleanLiteral = OR(['true', 'false']);
var IntegerLiteral = TEXT(LEX('hex-literal', OR([['0x', MANY(CHAR(hexCharSet))],
[MANY(digit)]])));
var FloatLiteral = TEXT(LEX('float-literal', [MANY(digit), '.', MANY(digit, min:0)]));
var Argument = g['Argument'];
var Module = g['Module'];
var Member = g['Member'];
var Interface = g['Interface'];
var ExceptionDef = g['ExceptionDef'];
var Type = g['Type'];
var TypeDef = g['TypeDef'];
var ImplStmt = g['ImplStmt'];
var ValueTypeDef = g['ValueTypeDef'];
var Const = g['Const'];
var Attribute = g['Attribute'];
var Operation = g['Operation'];
var Snippet = g['Snippet'];
var ExtAttrs = g['ExtAttrs'];
var MaybeExtAttrs = g['MaybeExtAttrs'];
var MaybeAnnotations = g['MaybeAnnotations'];
var ParentInterfaces = g['ParentInterfaces'];
final ScopedName = TEXT(LEX('scoped-name', MANY(CHAR(idStartCharSet + '_:.<>'))));
final ScopedNames = MANY(ScopedName, separator:',');
// Types
final IntegerTypeName = OR([
['byte', () => 'byte'],
['int', () => 'int'],
['long', 'long', () => 'long long'],
['long', () => 'long'],
['octet', () => 'octet'],
['short', () => 'short']]);
final IntegerType = OR([
['unsigned', IntegerTypeName, (name) => new IDLType('unsigned $name')],
[IntegerTypeName, (name) => new IDLType(name)]]);
final BooleanType = ['boolean', () => new IDLType('boolean')];
final OctetType = ['octet', () => new IDLType('octet')];
final FloatType = ['float', () => new IDLType('float')];
final DoubleType = ['double', () => new IDLType('double')];
final SequenceType = ['sequence', '<', Type, '>',
(type) => new IDLType('sequence', type)];
final ScopedNameType = [ScopedName, (name) => new IDLType(name)];
final NullableType =
[OR([IntegerType, BooleanType, OctetType, FloatType,
DoubleType, SequenceType, ScopedNameType]),
MAYBE('?'),
(type, nullable) =>
nullable ? new IDLType(type.id, type.parameter, true) : type];
final VoidType = ['void', () => new IDLType('void')];
final AnyType = ['any', () => new IDLType('any')];
final ObjectType = ['object', () => new IDLType('object')];
Type.def = OR([AnyType, ObjectType, NullableType]);
final ReturnType = OR([VoidType, Type]);
var Definition = syntax_switch(
WebIDL: OR([Module, Interface, ExceptionDef, TypeDef, ImplStmt,
ValueTypeDef, Const]),
WebKit: OR([Module, Interface]));
var Definitions = MANY(Definition, min:0);
Module.def = syntax_switch(
WebIDL: [MaybeExtAttrs, 'module', Id, '{', Definitions, '}',
SKIP(MAYBE(';')),
(ea, id, defs) => new IDLModule(id, ea, null, defs)],
WebKit: ['module', MaybeExtAttrs, Id, '{', Definitions, '}',
SKIP(MAYBE(';')),
(ea, id, defs) => new IDLModule(id, ea, null, defs)],
FremontCut: [MaybeAnnotations, MaybeExtAttrs, 'module', Id,
'{', Definitions, '}', SKIP(MAYBE(';')),
(ann, ea, id, defs) => new IDLModule(id, ea, ann, defs)]);
Interface.def = syntax_switch(
WebIDL: [MaybeExtAttrs, 'interface', Id, MAYBE(ParentInterfaces),
MAYBE(['{', MANY0(Member), '}']), ';',
(ea, id, p, ms) => new IDLInterface(id, ea, null, p, ms)],
WebKit: ['interface', MaybeExtAttrs, Id, MAYBE(ParentInterfaces),
MAYBE(['{', MANY0(Member), '}']), ';',
(ea, id, p, ms) => new IDLInterface(id, ea, null, p, ms)],
FremontCut: [MaybeAnnotations, MaybeExtAttrs, 'interface',
Id, MAYBE(ParentInterfaces),
MAYBE(['{', MANY0(Member), '}']), ';',
(ann, ea, id, p, ms) => new IDLInterface(id, ea, ann, p, ms)]);
Member.def = syntax_switch(
WebIDL: OR([Const, Attribute, Operation, ExtAttrs]),
WebKit: OR([Const, Attribute, Operation]),
FremontCut: OR([Const, Attribute, Operation, Snippet]));
var InterfaceType = ScopedName;
var ParentInterface = syntax_switch(
WebIDL: [InterfaceType],
WebKit: [InterfaceType],
FremontCut: [MaybeAnnotations, InterfaceType]);
ParentInterfaces.def = [':', MANY(ParentInterface, ',')];
// TypeDef (Web IDL):
TypeDef.def = ['typedef', Type, Id, ';', (type, id) => new IDLTypeDef(id, type)];
// TypeDef (Old-school W3C IDLs)
ValueTypeDef.def = ['valuetype', Id, Type, ';'];
// Implements Statement (Web IDL):
var ImplStmtImplementor = ScopedName;
var ImplStmtImplemented = ScopedName;
ImplStmt.def = [ImplStmtImplementor, 'implements', ImplStmtImplemented, ';'];
var ConstExpr = OR([BooleanLiteral, IntegerLiteral, FloatLiteral]);
Const.def = syntax_switch(
WebIDL: [MaybeExtAttrs, 'const', Type, Id, '=', ConstExpr, ';',
(ea, type, id, v) => new IDLConstant(id, type, ea, null, v)],
WebKit: ['const', MaybeExtAttrs, Type, Id, '=', ConstExpr, ';',
(ea, type, id, v) => new IDLConstant(id, type, ea, null, v)],
FremontCut: [MaybeAnnotations, MaybeExtAttrs,
'const', Type, Id, '=', ConstExpr, ';',
(ann, ea, type, id, v) =>
new IDLConstant(id, type, ea, ann, v)]);
// Attributes
var Stringifier = 'stringifier';
var AttrGetter = 'getter';
var AttrSetter = 'setter';
var ReadOnly = 'readonly';
var AttrGetterSetter = OR([AttrGetter, AttrSetter]);
var GetRaises = syntax_switch(
WebIDL: ['getraises', '(', ScopedNames, ')'],
WebKit: ['getter', 'raises', '(', ScopedNames, ')']);
var SetRaises = syntax_switch(
WebIDL: ['setraises', '(', ScopedNames, ')'],
WebKit: ['setter', 'raises', '(', ScopedNames, ')']);
var Raises = ['raises', '(', ScopedNames, ')'];
var AttrRaises = syntax_switch(
WebIDL: MANY(OR([GetRaises, SetRaises])),
WebKit: MANY(OR([GetRaises, SetRaises, Raises]), separator:','));
Attribute.def = syntax_switch(
WebIDL: [MaybeExtAttrs, MAYBE(Stringifier), MAYBE(ReadOnly),
'attribute', Type, Id, MAYBE(AttrRaises), ';'],
WebKit: [MAYBE(Stringifier), MAYBE(ReadOnly), 'attribute',
MaybeExtAttrs, Type, Id, MAYBE(AttrRaises), ';'],
FremontCut: [MaybeAnnotations, MaybeExtAttrs,
MAYBE(AttrGetterSetter), MAYBE(Stringifier), MAYBE(ReadOnly),
'attribute', Type, Id, MAYBE(AttrRaises), ';'
]);
// Operations
final Special = TEXT(OR(['getter', 'setter', 'creator', 'deleter', 'caller']));
final Specials = MANY(Special);
final Optional = 'optional';
final AnEllipsis = '...';
Argument.def = syntax_switch(
WebIDL: SEQ(MaybeExtAttrs, MAYBE(Optional), MAYBE(IN),
MAYBE(Optional), Type, MAYBE(AnEllipsis), Id,
(e, opt1, isin, opt2, type, el, id) =>
new IDLArgument(id, type, e, opt1 || opt2, isin, el)),
WebKit: SEQ(MAYBE(Optional), MAYBE('in'), MAYBE(Optional),
MaybeExtAttrs, Type, Id
(opt1, isin, opt2, e, type, id) =>
new IDLArgument(id, type, e, opt1 || opt2, isin, false)));
final Arguments = MANY0(Argument, ',');
Operation.def = syntax_switch(
WebIDL: [MaybeExtAttrs, MAYBE(Stringifier), MAYBE(Specials),
ReturnType, MAYBE(Id), '(', Arguments, ')', MAYBE(Raises), ';',
(ea, isStringifier, specials, type, id, args, raises) =>
new IDLOperation(id, type, ea, null, args, specials, isStringifier)
],
WebKit: [MaybeExtAttrs, ReturnType, MAYBE(Id), '(', Arguments, ')',
MAYBE(Raises), ';',
(ea, type, id, args, raises) =>
new IDLOperation(id, type, ea, null, args, [], false)
],
FremontCut: [MaybeAnnotations, MaybeExtAttrs, MAYBE(Stringifier),
MAYBE(Specials), ReturnType, MAYBE(Id), '(', Arguments, ')',
MAYBE(Raises), ';',
(ann, ea, isStringifier, specials, type, id, args, raises) =>
new IDLOperation(id, type, ea, ann, args, specials, isStringifier)
]);
// Exceptions
final ExceptionField = [Type, Id, ';'];
final ExceptionMember = OR([Const, ExceptionField, ExtAttrs]);
ExceptionDef.def = ['exception', Id, '{', MANY0(ExceptionMember), '}', ';'];
// ExtendedAttributes
var ExtAttrArgList = ['(', MANY0(Argument, ','), ')'];
var ExtAttrFunctionValue =
[Id, '(', MANY0(Argument, ','), ')',
(name, args) => new IDLExtAttrFunctionValue(name, args)
];
var ExtAttrValue = OR([ExtAttrFunctionValue,
TEXT(LEX('value', MANY(CHAR(idNextCharSet + '&:-|'))))]);
var ExtAttr = [Id, MAYBE(OR([['=', ExtAttrValue], ExtAttrArgList]))];
ExtAttrs.def = ['[', MANY(ExtAttr, ','), ']',
(list) => new IDLExtAttrs(list)];;
MaybeExtAttrs.def = OR(ExtAttrs,
[ () => new IDLExtAttrs() ] );
// Annotations - used in the FremontCut IDL grammar.
var AnnotationArgValue = TEXT(LEX('xx', MANY(CHAR(idNextCharSet + '&:-|'))));
var AnnotationArg = [Id, MAYBE(['=', AnnotationArgValue])];
var AnnotationBody = ['(', MANY0(AnnotationArg, ','), ')'];
var Annotation = ['@', Id, MAYBE(AnnotationBody),
(id, body) => new IDLAnnotation(id, body)];
MaybeAnnotations.def = [MANY0(Annotation), (list) => new IDLAnnotations(list)];
// Snippets - used in the FremontCut IDL grammar.
final SnippetText = TEXT(LEX('snippet body', MANY0([NOT('}'), CHAR()])));
Snippet.def = [MaybeAnnotations, 'snippet', '{', SnippetText, '}', ';',
(ann, text) => new IDLSnippet(ann, text)];
grammar.whitespace =
OR([MANY(CHAR(' \t\r\n')),
['//', MANY0([NOT(CHAR('\r\n')), CHAR()])],
['#', MANY0([NOT(CHAR('\r\n')), CHAR()])],
['/*', MANY0([NOT('*/'), CHAR()]), '*/']]);
// Top level - at least one definition.
return MANY(Definition);
}
}

View file

@ -1,115 +0,0 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#import('../../../utils/peg/pegparser.dart');
#source('idlparser.dart');
#source('idlrenderer.dart');
main() {
IDLParser parser = new IDLParser(FREMONTCUT_SYNTAX);
Grammar g = parser.grammar;
var Type = g['Type'];
show(g, Type, 'int');
show(g, Type, 'int ?');
show(g, Type, 'sequence<int?> ?');
show(g, Type, 'unsigned long long?');
show(g, Type, 'unsignedlonglong?');
var MaybeAnnotations = g['MaybeAnnotations'];
show(g, MaybeAnnotations, '');
show(g, MaybeAnnotations, '@Foo');
show(g, MaybeAnnotations, '@Foo @Bar');
show(g, MaybeAnnotations, '@Foo(A,B=1) @Bar');
var MaybeExtAttrs = g['MaybeExtAttrs'];
print(MaybeExtAttrs);
show(g, MaybeExtAttrs, '');
show(g, MaybeExtAttrs, '[A]');
var Module = g['Module'];
show(g, Module, 'module Harry { const int bob = 30;};');
show(g, Module, """
module Harry { [X,Y,Z=99] const int bob = 30; typedef x y;
interface Thing : SuperA, @Friendly SuperB {
[Nice] const unsigned long long kFoo = 12345;
[A,B,C,D,E] attribute int attr1;
[F=f(int a),K=99,DartName=Bert] int smudge(int a, int b, double x);
[X,Y,Z] int xyz([U,V] optional in optional int z);
[P,Q,R] int pqr();
int op1();
@Smurf @Beans(B=1,C,A=2) int op2();
snippet { yadda
yadda
};
};
//[A] const unsigned long long dweeb = 0xff;
};
""");
}
show(grammar, rule, input) {
print('show: "$input"');
var ast;
try {
ast = grammar.parse(rule, input);
} catch (var exception) {
if (exception is ParseError)
ast = exception;
else
throw;
}
print('${printList(ast)}');
print(render(ast));
}
void check(grammar, rule, input, expected) {
// If [expected] is String then the result is coerced to string.
// If [expected] is !String, the result is compared directly.
print('check: "$input"');
var ast;
try {
ast = grammar.parse(rule, input);
} catch (var exception) {
ast = exception;
}
var formatted = ast;
if (expected is String)
formatted = printList(ast);
Expect.equals(expected, formatted, "parse: $input");
}
// Prints the list in [1,2,3] notation, including nested lists.
void printList(item) {
if (item is List) {
StringBuffer sb = new StringBuffer();
sb.add('[');
var sep = '';
for (var x in item) {
sb.add(sep);
sb.add(printList(x));
sep = ',';
}
sb.add(']');
return sb.toString();
}
if (item == null)
return 'null';
return item.toString();
}

View file

@ -1,181 +0,0 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
List sorted(Iterable input, [compare, key]) {
comparator(compare, key) {
if (compare == null && key == null)
return (a, b) => a.compareTo(b);
if (compare == null)
return (a, b) => key(a).compareTo(key(b));
if (key == null)
return compare;
return (a, b) => compare(key(a), key(b));
}
List copy = new List.from(input);
copy.sort(comparator(compare, key));
return copy;
}
render(idl_node, [indent_str=' ']) {
var output = [''];
var indent_stack = [];
//indented(action()) {
indented(action) {
indent_stack.add(indent_str);
action();
indent_stack.removeLast();
}
sort(nodes) => sorted(nodes, key: (a) => a.id);
var w; // For some reason mutually recursive local functions don't work.
wln([node=null]) {
w(node);
output.add('\n');
}
w = (node, [list_separator]) {
/*
Writes the given node.
Args:
node -- a string, IDLNode instance or a list of such.
list_separator -- if provided, and node is a list,
list_separator will be written between the list items.
*/
if (node == null) {
return;
} else if (node is String) {
if (output.last().endsWith('\n'))
output.addAll(indent_stack);
output.add(node);
} else if (node is List) {
var separator = null;
for (var element in node) {
w(separator);
separator = list_separator;
w(element);
}
} else if (node is IDLFile) {
w(node.modules);
w(node.interfaces);
} else if (node is IDLModule) {
w(node.annotations);
w(node.extAttrs);
wln('module ${node.id} {');
indented(() {
w(node.interfaces);
w(node.typedefs);
});
wln('};');
} else if (node is IDLInterface) {
w(node.annotations);
w(node.extAttrs);
w('interface ${node.id}');
indented(() {
if (!node.parents.isEmpty()) {
wln(' :');
w(node.parents, ',\n');
}
wln(' {');
section(list, comment) {
if (list != null && !list.isEmpty()) {
wln();
wln(comment);
w(sort(list));
}
}
section(node.constants, '/* Constants */');
section(node.attributes, '/* Attributes */');
section(node.operations, '/* Operations */');
section(node.snippets, '/* Snippets */');
});
wln('};');
} else if (node is IDLParentInterface) {
w(node.annotations);
w(node.type.id);
} else if (node is IDLAnnotations) {
for (var name in sorted(node.map.getKeys())) {
IDLAnnotation annotation = node.map[name];
var args = annotation.map;
if (args.isEmpty()) {
w('@$name');
} else {
var formattedArgs = [];
for (var argName in sorted(args.getKeys())) {
var argValue = args[argName];
if (argValue == null)
formattedArgs.add(argName);
else
formattedArgs.add('$argName=$argValue');
}
w('@$name(${Strings.join(formattedArgs,',')})');
}
w(' ');
}
} else if (node is IDLExtAttrs) {
if(!node.map.isEmpty()) {
w('[');
var sep = null;
for (var name in sorted(node.map.getKeys())) {
w(sep);
sep = ', ';
w(name);
var value = node.map[name];
if (value != null) {
w('=');
w(value);
}
}
w('] ');
}
} else if (node is IDLAttribute) {
w(node.annotations);
w(node.extAttrs);
if (node.isFcGetter)
w('getter ');
if (node.isFcSetter)
w('setter ');
wln('attribute ${node.type.id} ${node.id};');
} else if (node is IDLConstant) {
w(node.annotations);
w(node.extAttrs);
wln('const ${node.type.id} ${node.id} = ${node.value};');
} else if (node is IDLSnippet) {
w(node.annotations);
wln('snippet {${node.text}};');
} else if (node is IDLOperation) {
w(node.annotations);
w(node.extAttrs);
if (node.specials != null && !node.specials.isEmpty()) {
w(node.specials, ' ');
w(' ');
}
w('${node.type.id} ${node.id}');
w('(');
w(node.arguments, ', ');
wln(');');
} else if (node is IDLArgument) {
w(node.extAttrs);
w('in ');
if (node.isOptional)
w('optional ');
w('${node.type.id} ${node.id}');
} else if (node is IDLExtAttrFunctionValue) {
w(node.name);
w('(');
w(node.arguments, ', ');
w(')');
} else if (node is IDLTypeDef) {
wln('typedef ${node.type.id} ${node.id};');
} else {
w('// $node\n');
}
};
w(idl_node);
return Strings.concatAll(output);
}

View file

@ -1,858 +0,0 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#library('Peg Parser');
/*
* The following functions are combinators for building Rules.
*
* A rule is one of the following
* - A String which matches the string literally.
* - A Symbol which matches the symbol's definition.
* - A list of rules with an optional reducing function, which matches a sequence.
* - The result of calling one of the combinators.
*
* Some rules are 'value-generating' rules, they return an 'abstract syntax
* tree' with the match. If a rule is not value-generating [:null:] is the
* value.
*
* A Symbol is always a value-generating rule. If the value is not required, use
* [:SKIP(aSymbol):] in place of [:aSymbol:].
*
* A String is not a value-generating rule but can be converted into one by
* using [:TEXT('string'):] in place of [:'string':].
*
* A list or sequence is value-generating depending on the subrules. The
* sequence is value-generating if any of the subrules are value-generating or
* if there is a reducing function. If no reducing function is given, the value
* returned depends on the number of value-generating subrules. If there is
* only one value generating subrule, that provideds the value for the sequence.
* If there are more, then the value is a list of the values of the
* value-generating subrules.
*/
/**
* Matches one character by a predicate on the character code.
* If [spec] is an int, that character is matched.
* If [spec] is a function it is used
*
* Example [: CHARCODE((code) => 48 <= code && code <= 57) :] recognizes an
* ASCII digit.
*
* CHARCODE does not generate a value.
*/
_Rule CHARCODE(spec, [name]) {
if (spec is int)
return new _CharCodeRule((code) => code == spec, name);
else
return new _CharCodeRule(spec, name);
}
/**
* Matches one of the [characters].
*
* CHAR does not generate a value.
*/
_Rule CHAR([characters]) {
if (characters == null)
return const _AnyCharRule();
if (characters is int)
return CHARCODE(characters);
// Find the range of character codes and construct an array of flags for codes
// within the range.
List<int> codes = characters.charCodes();
codes.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
int lo = codes[0];
int hi = codes[codes.length - 1];
if (lo == hi)
return CHARCODE(lo);
int len = hi - lo + 1;
var flags = new List<bool>(len);
for (int i = 0; i < len; ++i)
flags[i] = false;
for (int code in codes)
flags[code - lo] = true;
return CHARCODE((code) => code >= lo && code <= hi && flags[code - lo]);
}
/**
* Matches the end of the input.
*
* END does not generate a value.
*/
_Rule get END() => new _EndOfInputRule();
/**
* Throws an exception.
*/
_Rule ERROR(String message) => new _ErrorRule(message);
/**
* Matches [rule] but does not consume the input. Useful for matching a right
* context.
*
* AT does not generate a value.
*/
_Rule AT(rule) => new _ContextRule(_compile(rule));
/**
* Matches when [rule] does not match. No input is consumed.
*
* NOT does not generate a value.
*/
_Rule NOT(rule) => new _NegativeContextRule(_compile(rule));
/**
* Matches [rule] but generates no value even if [rule] generates a value.
*
* SKIP never generates a value.
*/
_Rule SKIP(rule) => new _SkipRule(_compile(rule));
/**
* Matches [rule] in a lexical context where whitespace is not automatically
* skipped. Useful for matching what would normally be considered to be tokens.
* [name] is a user-friendly description of what is being matched and is used in
* error messages.
*
* LEX(rule)
* LEX(name, rule)
*
* LEX does not generate a value. If a value is required, wrap LEX with TEXT.
*/
_Rule LEX(arg1, [arg2]) {
if (arg2 == null)
return new _LexicalRule(arg1 is String ? arg1 : null, _compile(arg1));
else
return new _LexicalRule(arg1, _compile(arg2));
}
/**
* Matches [rule] and generates a value from the matched text. If the [rule]
* matches, then TEXT(rule) matches and has a value derived from the string
* fragment that was matched. The default derived value is the string fragment.
*
* TEXT always generates a value.
*/
_Rule TEXT(rule, [extractor]) =>
new _TextValueRule(_compile(rule),
extractor == null
? (string, start, end) => string.substring(start, end)
: extractor);
/**
* Matches an optional rule.
*
* MAYBE is a value generating matcher.
*
* If [rule] is value generating then the value is the value generated by [rule]
* if it matches, and [:null:] if it does not.
*
* If [rule] is not value generatinge then the value is [:true:] if [rule]
* matches and [:false:] if it does not.
*/
_Rule MAYBE(rule) => new _OptionalRule(_compile(rule));
/**
* MANY(rule) matches [rule] [min] or more times.
* [min] must be 0 or 1.
* If [separator] is provided it is used to match a separator between matches of
* [rule].
*
* MANY is a value generating matcher. The value is a list of the matches of
* [rule]. The list may be empty if [:min == 0:].
*/
_Rule MANY(rule, [separator = null, int min = 1]) {
assert(0 <= min && min <= 1);
return new _RepeatRule(_compile(rule), _compileOptional(separator), min);
}
/**
* Matches [rule] zero or more times. Shorthand for [:MANY(rule, min:0):]
* TODO: retire min: parameter?
*
* MANY0 is a value generating matcher.
*/
_Rule MANY0(rule, [separator = null]) {
return new _RepeatRule(_compile(rule), _compileOptional(separator), 0);
}
/**
* Matches [rules] in order until one succeeds.
*
* OR is value-generating.
*/
_Rule OR([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]) =>
_compileMultiRule(
(a is List && b == null) // Backward compat. OR([a, b]) => OR(a, b).
? a
: _unspread(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z),
false,
(compiledRules, valueCount, reducer) =>
new _ChoiceRule(compiledRules));
_Rule SEQ([a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]) =>
_compile(_unspread(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z));
/**
* Matches [rule]
*/
_Rule MEMO(rule) => new _MemoRule(_compile(rule));
_Rule TAG(tag, rule) => _compile([rule, (ast) => [tag, ast]]);
class ParseError implements Exception {
const ParseError(String this._message);
String toString() => _message;
final String _message;
}
/**
* A grammar is a collection of symbols and rules that may be used to parse an
* input.
*/
class Grammar {
Map<String, Symbol> _symbols;
/** This rule may be set by the user to define whitespace. */
_Rule _whitespace;
_Rule get whitespace() => _whitespace;
void set whitespace(rule) { _whitespace = _compile(rule); }
Grammar() {
_symbols = new Map<String, Symbol>();
whitespace = CHAR(' \t\r\n');
}
/**
* operator [] is used to find or create symbols. Symbols may appear in rules
* to define recursive rules.
*/
Symbol operator [](String name) {
if (_symbols.containsKey(name))
return _symbols[name];
Symbol s = new Symbol(name, this);
_symbols[name] = s;
return s;
}
/**
* Parses the input string and returns the parsed AST, or throws an exception
* if the input can't be parsed.
*/
parse(root, String text) {
for (var symbol in _symbols.getValues())
if (symbol._rule == null)
print('${symbol.name} is undefined');
var state = new _ParserState(text, whitespace: whitespace);
var match = _compile(root).match(state, 0);
if (match == null)
return diagnose(state);
var pos = match[0];
pos = _skip_whitespace(state, pos);
if (pos == state._end)
return match[1];
// TODO: Make this complain about expecting end of file.
return diagnose(state);
}
diagnose(state) {
var message = 'unexpected error';
if (!state.max_rule.isEmpty()) {
var s = new Set();
for (var rule in state.max_rule)
s.add(rule.description());
var tokens = new List<String>.from(s);
tokens.sort((a, b) =>
a.startsWith("'") == b.startsWith("'")
? a.compareTo(b)
: a.startsWith("'") ? +1 : -1);
var expected = Strings.join(tokens, ' or ');
var found = state.max_pos == state._end ? 'end of file'
: "'${state._text[state.max_pos]}'";
message = 'Expected $expected but found $found';
}
int start = state.max_pos;
int end = start;
while (start >= 1 && state._text[start - 1] != '\n') --start;
while (end < state._text.length && state._text[end] != '\n') ++end;
var line = state._text.substring(start, end);
var indicator = '';
for (var i = 0; i < line.length && start + i < state.max_pos; i++)
indicator = indicator + ' ';
indicator = indicator + '^';
// TODO: Convert to an exception.
print(message);
print(line);
print(indicator);
return null;
}
}
class Symbol {
final String name;
final Grammar grammar;
_Rule _rule;
Symbol(this.name, this.grammar);
void set def(rule) {
assert(_rule == null); // Assign once.
_rule = _compile(rule);
}
toString() => _rule == null ? '<$name>' : '<$name = $_rule>';
}
class _ParserState {
_ParserState(this._text, [_Rule whitespace = null]) {
_end = this._text.length;
whitespaceRule = whitespace;
max_rule = [];
}
String _text;
int _end;
//
bool inWhitespaceMode = false;
_Rule whitespaceRule = null;
// Used for constructing an error message.
int inhibitExpectedTrackingDepth = 0;
int max_pos = 0;
var max_rule;
}
/**
* An interface tag for rules. If this tag is on a rule, then the description()
* of the rule is something sensible to put in a message.
*/
interface _Expectable {
String description();
}
class _Rule {
const _Rule();
// Returns null for a match failure or [pos, ast] for success.
match(_ParserState state, int pos) {
if (! state.inWhitespaceMode) {
pos = _skip_whitespace(state, pos);
}
return matchAfterWS(state, pos);
}
// Faster entry point for matching a sub-rule that is matched to the start
// position of the super-rule. Whitespace has already been skipped so no need
// to try to skip it again.
matchAfterWS(_ParserState state, int pos) {
if (state.inhibitExpectedTrackingDepth == 0) {
// Track position for possible error messaging
if (pos > state.max_pos) {
// Store position and the rule.
state.max_pos = pos;
if (this is _Expectable) {
state.max_rule = [this];
} else {
state.max_rule = [];
}
} else if (pos == state.max_pos) {
if (this is _Expectable) {
state.max_rule.add(this);
}
}
}
// Delegate the matching logic to the the specialized function.
return _match(state, pos);
}
// Overridden in subclasses to match the rule.
_match(_ParserState state, int pos) => null;
// Does the rule generate a value (AST) with the match?
bool get generatesValue() => false;
get defaultValue() => null;
}
int _skip_whitespace(state, pos) {
// Returns the next non-whitespace position.
// This is done by matching the optional whitespaceRule with the current text.
if (state.whitespaceRule == null)
return pos;
state.inWhitespaceMode = true;
state.inhibitExpectedTrackingDepth++;
while (true) {
var match = state.whitespaceRule.match(state, pos);
if (match == null)
break;
pos = match[0];
}
state.inWhitespaceMode = false;
state.inhibitExpectedTrackingDepth--;
return pos;
}
_Rule _compileOptional(rule) {
return rule == null ? null : _compile(rule);
}
_Rule _compile(rule) {
if (rule is _Rule)
return rule;
if (rule is String)
return new _StringRule(rule);
if (rule is Symbol)
return new _SymbolRule(rule);
if (rule is RegExp)
return new _RegExpRule(rule);
if (rule is List) {
return _compileMultiRule(
rule, true,
(compiledRules, valueCount, reducer) =>
new _SequenceRule(compiledRules, valueCount, reducer));
}
throw new Exception('Cannot compile rule: $rule');
}
class _EndOfInputRule extends _Rule {
_match(_ParserState state, int pos) {
if (pos == state._end)
return [pos, null];
return null;
}
toString() => 'END';
}
class _ErrorRule extends _Rule {
String message;
_ErrorRule(String this.message);
_match(_ParserState state, int pos) {
throw new ParseError(message);
}
toString() => 'ERROR($message)';
}
class _CharCodeRule extends _Rule {
Function _predicate;
var _name;
_CharCodeRule(this._predicate, this._name);
_match(_ParserState state, int pos) {
if (pos == state._end)
return null;
int code = state._text.charCodeAt(pos);
if (_predicate(code))
return [pos + 1, null];
return null;
}
toString() => _name == null ? 'CHARCODE($_predicate)' : 'CHARCODE($_name)';
}
class _AnyCharRule extends _Rule {
const _AnyCharRule();
_match(_ParserState state, int pos) {
if (pos == state._end)
return null;
return [pos + 1, null];
}
toString() => 'CHAR()';
}
class _SymbolRule extends _Rule {
final Symbol _symbol;
_SymbolRule(Symbol this._symbol);
_match(_ParserState state, int pos) {
if (_symbol._rule == null)
throw new Exception("Symbol '${_symbol.name}' is undefined");
return _symbol._rule.match(state, pos);
}
bool get generatesValue() => true;
toString() => '<${_symbol.name}>';
}
class _SkipRule extends _Rule {
// A rule that has no value.
_Rule _rule;
_SkipRule(_Rule this._rule);
_match(_ParserState state, int pos) {
var match = _rule.matchAfterWS(state, pos);
if (match == null)
return null;
return [match[0], null];
}
toString() => 'TOKEN($_rule)';
}
class _StringRule extends _Rule implements _Expectable {
final String _string;
int _len;
_StringRule(this._string) {
_len = _string.length;
}
_match(_ParserState state, int pos) {
if (pos + _len > state._end)
return null;
for (int i = 0; i < _len; i++) {
if (state._text.charCodeAt(pos + i) != _string.charCodeAt(i))
return null;
}
return [pos + _len, null];
}
//get defaultValue() => _string;
toString() => '"$_string"';
description() => "'$_string'";
}
class _RegExpRule extends _Rule {
RegExp _re;
_RegExpRule(this._re) {
// There is no convenient way to match an anchored substring.
throw new Exception('RegExp matching not supported');
}
toString() => '"$_re"';
}
class _LexicalRule extends _Rule implements _Expectable {
final String _name;
final _Rule _rule;
_LexicalRule(String this._name, _Rule this._rule);
_match(_ParserState state, int pos) {
state.inWhitespaceMode = true;
state.inhibitExpectedTrackingDepth++;
var match = _rule.matchAfterWS(state, pos);
state.inhibitExpectedTrackingDepth--;
state.inWhitespaceMode = false;
return match;
}
toString() => _name;
description() => _name == null ? '?' : _name;
}
class _TextValueRule extends _Rule {
final _Rule _rule;
final _extract; // Function
_TextValueRule(_Rule this._rule, Function this._extract);
_match(_ParserState state, int pos) {
var match = _rule.matchAfterWS(state, pos);
if (match == null) {
return null;
}
var endPos = match[0];
return [endPos, _extract(state._text, pos, endPos)];
}
bool get generatesValue() => true;
toString() => 'TEXT($_rule)';
}
_Rule _compileMultiRule(List rules,
bool allowReducer,
finish(compiledRules, valueCount, reducer)) {
int valueCount = 0;
List compiledRules = new List<_Rule>();
Function reducer;
for (var rule in rules) {
if (reducer != null)
throw new Exception('Reducer must be last in sequence: $rule');
if (rule is Function) {
if (allowReducer)
reducer = rule;
else
throw new Exception('Bad rule: "$rule"');
} else {
_Rule compiledRule = _compile(rule);
if (compiledRule.generatesValue)
++valueCount;
compiledRules.add(compiledRule);
}
}
return finish(compiledRules, valueCount, reducer);
}
String _formatMultiRule(String functor, List rules) {
var sb = new StringBuffer(functor);
sb.add('(');
var separator = '';
for (var rule in rules) {
sb.add(separator);
sb.add(rule);
separator = ',';
}
sb.add(')');
return sb.toString();
}
class _SequenceRule extends _Rule {
// This rule matches the component rules in order.
final List<_Rule> _rules;
final int _generatingSubRules = 0;
final Function _reducer;
bool _generatesValue;
_SequenceRule(List<_Rule> this._rules,
int this._generatingSubRules,
Function this._reducer) {
_generatesValue = _generatingSubRules > 0 || _reducer != null;
}
_match(state, pos) {
var sequence = [];
for (var rule in _rules) {
var match = rule.match(state, pos);
if (match == null)
return null;
if (rule.generatesValue) {
var ast = match[1];
sequence.add(ast);
}
pos = match[0];
}
if (_reducer == null) {
if (_generatingSubRules == 0)
return [pos, null];
if (_generatingSubRules == 1)
return [pos, sequence[0]];
return [pos, sequence];
} else {
return [pos, _apply(_reducer, sequence)];
}
}
bool get generatesValue() => _generatesValue;
toString() => _formatMultiRule('SEQ', _rules);
}
class _ChoiceRule extends _Rule {
// This rule matches the first component rule that matches.
List<_Rule> _rules;
_ChoiceRule(List<_Rule> this._rules);
_match(state, pos) {
for (var rule in _rules) {
var match = rule.match(state, pos);
if (match != null) {
/*
if (!rule.generatesValue) {
var value = rule.defaultValue;
if (value != null)
return [match[0], value];
}
*/
return match;
}
}
return null;
}
bool get generatesValue() => true;
toString() => _formatMultiRule('OR', _rules);
}
class _OptionalRule extends _Rule {
_Rule _rule;
_OptionalRule(_Rule this._rule);
_match(_ParserState state, int pos) {
var match = _rule.match(state, pos);
if (_rule.generatesValue)
return match == null
? [pos, null]
: match;
return match == null
? [pos, false]
: [match[0], true];
}
bool get generatesValue() => true;
toString() => 'MAYBE($_rule)';
}
class _ContextRule extends _Rule {
_Rule _rule;
_ContextRule(_Rule this._rule);
_match(_ParserState state, int pos) {
// TODO: protect error state.
var match = _rule._match(state, pos);
if (match == null)
return null;
return [pos, null];
}
toString() => 'AT($_rule)';
}
class _NegativeContextRule extends _Rule {
_Rule _rule;
_NegativeContextRule(_Rule this._rule);
_match(_ParserState state, int pos) {
// TODO: protect error state.
var match = _rule._match(state, pos);
if (match == null)
return [pos, null];
return null;
}
toString() => 'NOT($_rule)';
}
class _RepeatRule extends _Rule {
// Matches zero, one or more items.
_Rule _rule;
_Rule _separator;
int _min;
_RepeatRule(this._rule, this._separator, this._min);
_match(state, pos) {
// First match.
var match = _rule.match(state, pos);
if (match == null)
if (_min == 0)
return [pos, []];
else
return null;
pos = match[0];
var result = [match[1]];
// Subsequent matches:
while (true) {
var newPos = pos;
if (_separator != null) {
match = _separator.match(state, pos);
if (match == null)
return [pos, result];
newPos = match[0];
}
match = _rule.match(state, newPos);
if (match == null)
return [pos, result];
pos = match[0];
result.add(match[1]);
}
}
bool get generatesValue() => true;
toString() => 'MANY(min:$_min, $_rule${_separator==null?'':", sep: $_separator"})';
}
class _MemoRule extends _Rule {
final _Rule _rule;
var parseInstance;
// A map from position to result. Can this be replaced with something
// smaller?
// TODO: figure out how to discard the map and parseInstance after parsing.
Map<int,Object> map;
_MemoRule(this._rule);
_match(state, pos) {
// See if we are still parsing the same input. Relies on the fact that the
// input is a string and strings are immutable.
if (parseInstance !== state._text) {
map = new Map<int,Object>();
parseInstance = state._text;
}
// TODO: does this have to check or preserve parse state (like
// inWhitespaceMode, error position info etc?)
// Stored result can be null (memoized failure).
if (map.containsKey(pos)) {
return map[pos];
}
var match = _rule.match(state, pos);
map[pos] = match;
return match;
}
bool get generatesValue() => _rule.generatesValue;
toString() => 'MEMO($_rule)';
}
_apply(fn, List args) {
switch (args.length) {
case 0: return fn();
case 1: return fn(args[0]);
case 2: return fn(args[0], args[1]);
case 3: return fn(args[0], args[1], args[2]);
case 4: return fn(args[0], args[1], args[2], args[3]);
case 5: return fn(args[0], args[1], args[2], args[3], args[4]);
case 6: return fn(args[0], args[1], args[2], args[3], args[4],
args[5]);
case 7: return fn(args[0], args[1], args[2], args[3], args[4],
args[5], args[6]);
case 8: return fn(args[0], args[1], args[2], args[3], args[4],
args[5], args[6], args[7]);
case 9: return fn(args[0], args[1], args[2], args[3], args[4],
args[5], args[6], args[7], args[8]);
case 10: return fn(args[0], args[1], args[2], args[3], args[4],
args[5], args[6], args[7], args[8], args[9]);
default:
throw new Exception('Too many arguments in _apply: $args');
}
}
List _unspread(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z) {
List list = new List();
add(element) { if (element != null) list.add(element); }
add(a);
add(b);
add(c);
add(d);
add(e);
add(f);
add(g);
add(h);
add(i);
add(j);
add(k);
add(l);
add(m);
add(n);
add(o);
add(p);
add(q);
add(r);
add(s);
add(t);
add(u);
add(v);
add(w);
add(x);
add(y);
add(z);
return list;
}

View file

@ -1,346 +0,0 @@
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#import('pegparser.dart');
testParens() {
Grammar g = new Grammar();
Symbol a = g['A'];
a.def = ['(', MANY(a, min:0), ')', (a) => a];
check(g, a, "", null);
check(g, a, "()", '[]');
check(g, a, "(()())", '[[],[]]');
check(g, a, "(()((()))())", '[[],[[[]]],[]]');
}
testBlockComment() {
// Block comment in whitespace.
Grammar g = new Grammar();
Symbol blockComment = g['blockComment'];
blockComment.def =
['/*',
MANY(OR([blockComment,
[NOT('*/'), CHAR()],
[END, ERROR('EOF in block comment')]
]),
min: 0),
'*/'];
print(blockComment);
var a = MANY(TEXT('x'));
g.whitespace = OR([g.whitespace, blockComment]);
check(g, a, "x /**/ x", '[x,x]');
check(g, a, "x /*/**/*/ x", '[x,x]');
check(g, a, "x /*/***/ x", 'EOF in block comment');
check(g, a, "x /*/*/x**/**/ x", '[x,x]');
check(g, a, @"""
/* Comment */
/* Following comment with /* nested comment*/ */
x
/* x in comment */
x /* outside comment */
""",
'[x,x]');
}
testTEXT() {
Grammar g = new Grammar();
// TEXT grabs the parsed text,
check(g, TEXT(LEX(MANY(OR(['1','a'])))), ' 1a1 ', '1a1');
// Without the lexical context, TEXT will grab intervening whitespace.
check(g, TEXT(MANY(OR(['1','a']))), ' 1a1 ', '1a1');
check(g, TEXT(MANY(OR(['1','a']))), ' 1 a 1 ', '1 a 1');
// Custom processing of the TEXT substring.
var binaryNumber =
TEXT(LEX(MANY(OR(['0','1']))),
(str, start, end) {
var r = 0;
var zero = '0'.charCodeAt(0);
for (int i = start; i < end; i++)
r = r * 2 + (str.charCodeAt(i) - zero);
return r;
});
check(g, binaryNumber, ' 10101 ', 21);
check(g, binaryNumber, '1010111', 87);
check(g, binaryNumber, '1010 111', null);
}
testOR() {
// OR matches the first match.
Grammar g = new Grammar();
check(g, OR([['a', NOT(END), () => 1],
['a', () => 2],
['a', () => 3]]),
'a', 2);
}
testCODE() {
Grammar g = new Grammar();
var a = TEXT(LEX('thing', MANY(CHAR('bcd'))));
check(g, a, 'bbb', 'bbb');
check(g, a, 'ccc', 'ccc');
check(g, a, 'ddd', 'ddd');
check(g, a, 'bad', null); // a is outside range.
check(g, a, 'bed', null); // e is outside range.
}
testC() {
// Curried tree builders.
binary(operation) => (second) => (first) => [operation, first, second];
unary(operation) => () => (first) => [operation, first];
reform(a, fns) {
var r = a;
for (var fn in fns)
r = fn(r);
return r;
}
Grammar g = new Grammar();
Symbol expression = g['expression'];
Symbol postfix_e = g['postfix_e'];
Symbol unary_e = g['unary_e'];
Symbol cast_e = g['cast_e'];
Symbol mult_e = g['mult_e'];
Symbol add_e = g['add_e'];
Symbol shift_e = g['shift_e'];
Symbol relational_e = g['relational_e'];
Symbol equality_e = g['equality_e'];
Symbol cond_e = g['cond_e'];
Symbol assignment_e = g['assignment_e'];
// Lexical elements.
var idStartChar = CHAR(
@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
var idNextChar = CHAR(
@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$_");
var id = TEXT(LEX('identifier', [idStartChar, MANY(idNextChar, min: 0)]));
var lit = TEXT(LEX('literal', MANY(CHAR('0123456789'))));
var type_name = id;
// Expression grammar.
var primary_e = OR([id,
lit,
['(', expression, ')', (e) => e]
]);
var postfixes = OR([['(', MANY(assignment_e, ',', 0), ')', binary('apply')],
['++', unary('postinc')],
['--', unary('postdec')],
['.', id, binary('field')],
['->', id, binary('ptr')],
]);
postfix_e.def = [primary_e, MANY(postfixes, min:0), reform];
var unary_op = OR([['&', () => 'address'],
['*', () => 'indir'],
['!', () => 'not'],
['~', () => 'not'],
['-', () => 'negate'],
['+', () => 'uplus'],
]);
var sizeof = LEX('sizeof', ['sizeof', NOT(idNextChar)]);
Symbol unary_e_plain = g['unary_e_plain'];
unary_e_plain.def =
OR([ ['++', unary_e, (e) => ['preinc', e]],
['--', unary_e, (e) => ['predec', e]],
[unary_op, cast_e, (o, e) => [o, e]],
[sizeof, unary_e, (e) => ['sizeof-expr', e]],
[sizeof, '(', type_name , ')', (t) => ['sizeof-type', t]],
postfix_e
]);
unary_e.def = MEMO(unary_e_plain);
//unary_e.def = unary_e_plain;
cast_e.def = OR([ ['(', type_name, ')', cast_e, (t, e) => ['cast', t, e]],
unary_e,
]);
var mult_ops = OR([['*', cast_e, binary('mult')],
['/', cast_e, binary('div')],
['%', cast_e, binary('rem')],
]);
mult_e.def = [cast_e, MANY(mult_ops, min:0), reform];
var add_ops = OR([['+', mult_e, binary('add')],
['-', mult_e, binary('sub')],
]);
add_e.def = [mult_e, MANY(add_ops, min:0), reform];
var shift_ops = OR([['>>', add_e, binary('shl')],
['<<', add_e, binary('shr')],
]);
shift_e.def = [add_e, MANY(shift_ops, min:0), reform];
var relational_ops = OR([['<=', shift_e, binary('le')],
['>=', shift_e, binary('ge')],
['<', shift_e, binary('lt')],
['>', shift_e, binary('gt')],
]);
relational_e.def = [shift_e, MANY(relational_ops, min:0), reform];
var equality_ops = OR([['==', shift_e, binary('eq')],
['!=', shift_e, binary('ne')],
]);
equality_e.def = [relational_e, MANY(equality_ops, min:0), reform];
var bit_and_op = LEX('&', ['&', NOT('&')]); // Don't see '&&' and '&', '&'
var bit_or_op = LEX('|', ['|', NOT('|')]);
var and_e = [equality_e, MANY([bit_and_op, equality_e, binary('bitand')], min:0), reform];
var xor_e = [and_e, MANY(['^', and_e, binary('bitxor')], min:0), reform];
var or_e = [xor_e, MANY([bit_or_op, xor_e, binary('bitor')], min:0), reform];
var log_and_e = [or_e, MANY(['&&', or_e, binary('and')], min:0), reform];
var log_or_e = [log_and_e, MANY(['||', log_and_e, binary('or')], min:0), reform];
//cond_e.def = OR([ [log_or_e, '?', expression, ':', cond_e,
// (p,a,b) => ['cond', p, a, b]],
// log_or_e]);
// Alternate version avoids reparsing log_or_e.
cond_e.def = [log_or_e, MAYBE(['?', expression, ':', cond_e]),
(p, r) => r == null || r == false ? p : ['cond', p, r[0], r[1]]];
var assign_op = OR([['*=', () => 'mulassign'],
['=', () => 'assign']]);
// TODO: Figure out how not to re-parse a unary_e.
// Order matters - cond_e can't go first since cond_e will succeed on, e.g. 'a'.
assignment_e.def = OR([[unary_e, assign_op, assignment_e,
(u, op, a) => [op, u, a]],
cond_e]);
expression.def = [assignment_e,
MANY([',', assignment_e, binary('comma')], min:0),
reform];
show(g, expression, 'a');
check(g, expression, 'a', 'a');
check(g, expression, '(a)', 'a');
check(g, expression, ' ( ( a ) ) ', 'a');
check(g, expression, 'a(~1,2)', '[apply,a,[[not,1],2]]');
check(g, expression, 'a(1)(x,2)', '[apply,[apply,a,[1]],[x,2]]');
check(g, expression, 'a(1,2())', '[apply,a,[1,[apply,2,[]]]]');
check(g, expression, '++a++', '[preinc,[postinc,a]]');
check(g, expression, 'a++++b', null);
check(g, expression, 'a++ ++b', null);
check(g, expression, 'a+ +++b', '[add,a,[preinc,[uplus,b]]]');
check(g, expression, 'a+ + ++b', '[add,a,[uplus,[preinc,b]]]');
check(g, expression, 'a+ + + +b', '[add,a,[uplus,[uplus,[uplus,b]]]]');
check(g, expression, 'a+ ++ +b', '[add,a,[preinc,[uplus,b]]]');
check(g, expression, 'a++ + +b', '[add,[postinc,a],[uplus,b]]');
check(g, expression, 'a+++ +b', '[add,[postinc,a],[uplus,b]]');
check(g, expression, '((T)f)(x)', '[apply,[cast,T,f],[x]]');
check(g, expression, '(T)f(x)', '[cast,T,[apply,f,[x]]]');
check(g, expression, 'a++*++b', '[mult,[postinc,a],[preinc,b]]');
check(g, expression, 'a<<1>>++b', '[shl,[shr,a,1],[preinc,b]]');
check(g, expression, 'a<1&&b', '[and,[lt,a,1],b]');
check(g, expression, 'a<1 & &b', '[bitand,[lt,a,1],[address,b]]');
check(g, expression,
'a ? b ? c : d : e ? f : g',
'[cond,a,[cond,b,c,d],[cond,e,f,g]]');
check(g, expression, 'a,b,c', '[comma,[comma,a,b],c]');
check(g, expression, 'a=1,b,c', '[comma,[comma,[assign,a,1],b],c]');
check(g, expression,
'((((((((((((a))))))))))))=1,b,c', '[comma,[comma,[assign,a,1],b],c]');
check(g, expression, 'sizeof a', '[sizeof-expr,a]');
check(g, expression, 'sizeofa', 'sizeofa');
check(g, expression, 'sizeof (a)', '[sizeof-expr,a]');
}
show(grammar, rule, input) {
print('show: "$input"');
var ast;
try {
ast = grammar.parse(rule, input);
} catch (var exception) {
if (exception is ParseError)
ast = exception;
else
throw;
}
print('${printList(ast)}');
}
void check(grammar, rule, input, expected) {
// If [expected] is String then the result is coerced to string.
// If [expected] is !String, the result is compared directly.
print('check: "$input"');
var ast;
try {
ast = grammar.parse(rule, input);
} catch (var exception) {
ast = exception;
}
var formatted = ast;
if (expected is String)
formatted = printList(ast);
Expect.equals(expected, formatted, "parse: $input");
}
// Prints the list in [1,2,3] notation, including nested lists.
void printList(item) {
if (item is List) {
StringBuffer sb = new StringBuffer();
sb.add('[');
var sep = '';
for (var x in item) {
sb.add(sep);
sb.add(printList(x));
sep = ',';
}
sb.add(']');
return sb.toString();
}
if (item == null)
return 'null';
return item.toString();
}
main() {
testCODE();
testParens();
testOR();
testTEXT();
testBlockComment();
testC();
}