mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
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:
parent
5801bcfbb3
commit
9aacf5aa62
5 changed files with 0 additions and 2047 deletions
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
Loading…
Reference in a new issue