parser_combinator 0.1.3
parser_combinator: ^0.1.3 copied to clipboard
Parser combinator is a collection of parsers that can be used to combine basic parsers to create parsers for more complex rules.
example/example.dart
import 'package:parser_combinator/parser/choice.dart';
import 'package:parser_combinator/parser/delimited.dart';
import 'package:parser_combinator/parser/digit1.dart';
import 'package:parser_combinator/parser/eof.dart';
import 'package:parser_combinator/parser/expected.dart';
import 'package:parser_combinator/parser/fast.dart';
import 'package:parser_combinator/parser/handle_error.dart';
import 'package:parser_combinator/parser/integer.dart';
import 'package:parser_combinator/parser/mapped.dart';
import 'package:parser_combinator/parser/opt.dart';
import 'package:parser_combinator/parser/preceded.dart';
import 'package:parser_combinator/parser/predicate.dart';
import 'package:parser_combinator/parser/recognize.dart';
import 'package:parser_combinator/parser/ref.dart';
import 'package:parser_combinator/parser/separated_list.dart';
import 'package:parser_combinator/parser/separated_pair.dart';
import 'package:parser_combinator/parser/skip16_while.dart';
import 'package:parser_combinator/parser/string_chars.dart';
import 'package:parser_combinator/parser/tag.dart';
import 'package:parser_combinator/parser/tags.dart';
import 'package:parser_combinator/parser/take_while_m_n.dart';
import 'package:parser_combinator/parser/terminated.dart';
import 'package:parser_combinator/parser/tuple.dart';
import 'package:parser_combinator/parser/value.dart';
import 'package:parser_combinator/parser_combinator.dart';
import 'package:parser_combinator/parsing.dart';
import 'package:parser_combinator/runtime.dart';
void main(List<String> args) {
final result = parseString(parser, '{"rocket": "🚀 flies to the stars"}');
print(result);
}
const parser = Delimited(name: 'json', _ws, _value, Eof());
const value = Choice7(
name: 'value',
_object,
_array,
_string,
_number,
_false,
_null,
_true,
);
const _array = Delimited(name: 'array', _openBracket, _values, _closeBracket);
const _closeBrace = Terminated(name: '_closeBrace', Tag('}'), _ws);
const _closeBracket = Terminated(name: '_closeBracket', Tag(']'), _ws);
const _colon = Terminated(name: '_colon', Tag(':'), _ws);
const _comma = Terminated(name: '_comma', Tag(','), _ws);
const _digit1 = Expected(name: '_digit1', Digit1(), 'decimal digit');
const _doubleQuote = Terminated(name: '_doubleQuote', Tag('"'), _ws);
const _escape = _Escape(name: '_escape');
const _escapeHexValue = Mapped(
name: '_escapeHexValue',
Preceded(Tag('u'), _hexValueChecked),
createStringFromHexValue);
const _exponent =
Tuple3(name: '_exponent', Tags(['E', 'e']), Opt(Tags(['-', '+'])), _digit1);
const _false = ValueP(name: '_false', false, Terminated(Tag('false'), _ws));
const _fraction = Tuple2(name: '_fraction', Tag('.'), _digit1);
const _hexValue = TakeWhileMN(name: '_hexValue', 4, 4, isHexDigit);
const _hexValueChecked =
HandleError(name: '_hexValueChecked', _hexValue, handleHexValueError);
const _integer = Integer(name: '_integer');
const _keyValue = Mapped(
name: '_keyValue', SeparatedPair(_string, _colon, _value), createMapEntry);
const _keyValues = SeparatedList(name: '_keyValues', _keyValue, _comma);
const _null =
// ignore: unnecessary_cast
ValueP(name: '_null', null as Object?, Terminated(Tag('null'), _ws));
const _number = Expected(name: 'number', Terminated(_number_, _ws), 'number');
/// '-'?('0'|[1-9][0-9]*)('.'[0-9]+)?([eE][+-]?[0-9]+)?
const _number_ = Mapped(
name: 'number_',
Recognize(
Fast3(
_integer,
Opt(_fraction),
Opt(_exponent),
),
),
num.parse);
const _object = Mapped(
name: 'object',
Delimited(_openBrace, _keyValues, _closeBrace),
Map.fromEntries);
const _openBrace = Terminated(name: '_openBrace', Tag('{'), _ws);
const _openBracket = Terminated(name: '_openBracket', Tag('['), _ws);
const _string = Expected(
name: 'string',
Delimited(
Tag('"'),
StringChars(
isNormalChar,
92,
Choice2(
_escape,
_escapeHexValue,
),
),
_doubleQuote,
),
'string',
);
const _true = ValueP(name: '_true', true, Terminated(Tag('true'), _ws));
const Parser<String, Object?> _value = Ref(name: '_value', getValueParser);
const _values = SeparatedList(name: '_values', _value, _comma);
const _ws = Skip16While(name: '_ws', isWhitespace);
MapEntry<String, Object?> createMapEntry((String, Object?) kv) {
return MapEntry(kv.$1, kv.$2);
}
String createStringFromHexValue(String s) {
return String.fromCharCode(toHexValue(s));
}
Parser<String, Object?> getValueParser() => value;
(bool, List<ParseError>) handleHexValueError(int start, int end) {
return (
false,
[ErrorMessage(start - end, 'Expected 4 digit hexadecimal number')]
);
}
bool isNormalChar(int c) => c <= 91
? c <= 33
? c >= 32
: c >= 35
: c <= 1114111 && c >= 93;
@pragma('vm:prefer-inline')
int toHexValue(String s) {
var r = 0;
for (var i = s.length - 1, j = 0; i >= 0; i--, j += 4) {
final c = s.codeUnitAt(i);
final int v;
if (c >= 0x30 && c <= 0x39) {
v = c - 0x30;
} else if (c >= 0x41 && c <= 0x46) {
v = c - 0x41 + 10;
} else if (c >= 0x61 && c <= 0x66) {
v = c - 0x61 + 10;
} else {
throw StateError('Internal error');
}
r += v * (1 << j);
}
return r;
}
class ParserErrorMessages {
static const expected4DigitHexadecimalNumber =
'Expected 4 digit hexadecimal number';
final String text;
const ParserErrorMessages(this.text);
}
class _Escape extends Parser<String, String> {
const _Escape({String? name}) : super(name);
@override
Parser<String, String> build(ParserBuilder builder) {
return _Escape(name: name);
}
@override
bool fastParse(State<String> state) {
final r = parse(state);
return r != null;
}
@override
Result<String>? parse(State<String> state) {
final c = state.input.codeUnitAt(state.pos);
switch (c) {
case 34:
state.pos++;
return const Result('"');
case 0x2F:
state.pos++;
return const Result('/');
case 0x5C:
state.pos++;
return const Result(r'\\');
case 0x62:
state.pos++;
return const Result('\b');
case 0x66:
state.pos++;
return const Result('\f');
case 0x6E:
state.pos++;
return const Result('\n');
case 0x72:
state.pos++;
return const Result('\r');
case 0x74:
state.pos++;
return const Result('\t');
}
return state.fail(const ErrorUnexpectedCharacter());
}
}