Created
April 5, 2013 00:50
-
-
Save takeshik/5315771 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Yacq | |
private static readonly Grammar _standard = new Grammar(); | |
public static Grammar Standard | |
{ | |
get | |
{ | |
return _standard; | |
} | |
} | |
static Grammar() | |
{ | |
var newline = Combinator.Choice( | |
Chars.Sequence("\r\n"), | |
Chars.OneOf('\r', '\n', '\x85', '\u2028', '\u2029') | |
.Select(EnumerableEx.Return) | |
).Select(_ => Environment.NewLine); | |
var punctuation = Chars.OneOf('"', '\'', '(', ')', ',', '.', ':', ';', '[', ']', '`', '{', '}'); | |
Parser<char, YacqExpression> expressionRef = null; | |
var expression = new Lazy<Parser<Char, YacqExpression>>( | |
() => stream => expressionRef(stream) | |
); | |
_standard.Add("yacq", "expression", g => expression.Value); | |
// Comments | |
{ | |
_standard.Add("comment", "eol", g => Prims.Pipe( | |
';'.Satisfy(), | |
newline.Not().Right(Chars.Any()).Many(), | |
newline.Ignore().Or(Chars.Eof()), | |
(p, r, s) => (YacqExpression) YacqExpression.Ignore() | |
)); | |
Parser<char, YacqExpression> blockCommentRef = null; | |
Parser<char, YacqExpression> blockCommentRestRef = null; | |
var blockComment = new Lazy<Parser<Char, YacqExpression>>( | |
() => stream => blockCommentRef(stream) | |
); | |
var blockCommentRest = new Lazy<Parser<Char, YacqExpression>>( | |
() => stream => blockCommentRestRef(stream) | |
); | |
var blockCommentPrefix = Chars.Sequence("#|"); | |
var blockCommentSuffix = Chars.Sequence("|#"); | |
blockCommentRef = blockCommentPrefix | |
.Right(blockCommentRest.Value.Many()) | |
.Left(blockCommentSuffix) | |
.Select(_ => (YacqExpression) YacqExpression.Ignore()); | |
blockCommentRestRef = blockCommentPrefix.Not() | |
.Right(blockCommentSuffix.Not()) | |
.Right(Chars.Any()) | |
.Select(_ => (YacqExpression) YacqExpression.Ignore()) | |
.Or(blockComment.Value); | |
_standard.Add("comment", "block", g => blockComment.Value); | |
_standard.Add("comment", "expression", g => Prims.Pipe( | |
Chars.Sequence("#;"), | |
g["yacq", "expression"], | |
(p, r) => (YacqExpression) YacqExpression.Ignore() | |
)); | |
_standard.Add("yacq", "comment", g => Combinator.Choice(g["comment"])); | |
} | |
var ignore = Combinator.Choice( | |
_standard.Get["yacq", "comment"].Ignore(), | |
Chars.Space().Ignore(), | |
newline.Ignore() | |
).Many(); | |
// Texts | |
_standard.Add("term", "text", g => Span( | |
Chars.OneOf('\'', '\"', '`') | |
.SelectMany(q => q.Satisfy() | |
.Not() | |
.Right('\\'.Satisfy() | |
.Right(q.Satisfy()) | |
.Or(Chars.Any()) | |
) | |
.Many() | |
.Select(cs => cs.StartWith(q).EndWith(q)) | |
) | |
.Select(cs => (YacqExpression) YacqExpression.Text(new String(cs.ToArray()))), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Numbers | |
{ | |
var numberPrefix = Chars.OneOf('+', '-'); | |
var numberSuffix = Combinator.Choice( | |
Chars.Sequence("ul"), | |
Chars.Sequence("UL"), | |
Chars.OneOf('D', 'F', 'L', 'M', 'U', 'd', 'f', 'l', 'm', 'u') | |
.Select(EnumerableEx.Return) | |
); | |
var digit = '_'.Satisfy().Many().Right(Chars.Digit()); | |
var hexPrefix = Chars.Sequence("0x"); | |
var hex = '_'.Satisfy().Many().Right(Chars.Hex()); | |
var octPrefix = Chars.Sequence("0o"); | |
var oct = '_'.Satisfy().Many().Right(Chars.Oct()); | |
var binPrefix = Chars.Sequence("0b"); | |
var bin = '_'.Satisfy().Many().Right(Chars.OneOf('0', '1')); | |
var fraction = Prims.Pipe( | |
'.'.Satisfy(), | |
digit.Many(1), | |
(d, ds) => ds.StartWith(d) | |
); | |
var exponent = Prims.Pipe( | |
Chars.OneOf('E', 'e'), | |
Chars.OneOf('+', '-').Maybe(), | |
digit.Many(1), | |
(e, s, ds) => ds | |
.If(_ => s.Exists(), _ => _.StartWith(s.Perform())) | |
.StartWith(e) | |
); | |
_standard.Add("term", "number", g => Combinator.Choice( | |
Span(Prims.Pipe( | |
binPrefix, | |
bin.Many(1), | |
numberSuffix.Maybe(), | |
(p, n, s) => (YacqExpression) YacqExpression.Number( | |
new String(p.Concat(n).If( | |
_ => s.Exists(), | |
cs => cs.Concat(s.Perform()) | |
).ToArray()) | |
) | |
), (start, end, t) => t.SetPosition(start, end)), | |
Span(Prims.Pipe( | |
octPrefix, | |
oct.Many(1), | |
numberSuffix.Maybe(), | |
(p, n, s) => (YacqExpression) YacqExpression.Number( | |
new String(p.Concat(n).If( | |
_ => s.Exists(), | |
cs => cs.Concat(s.Perform()) | |
).ToArray()) | |
) | |
), (start, end, t) => t.SetPosition(start, end)), | |
Span(Prims.Pipe( | |
hexPrefix, | |
hex.Many(1), | |
numberSuffix.Maybe(), | |
(p, n, s) => (YacqExpression) YacqExpression.Number( | |
new String(p.Concat(n).If( | |
_ => s.Exists(), | |
cs => cs.Concat(s.Perform()) | |
).ToArray()) | |
) | |
), (start, end, t) => t.SetPosition(start, end)), | |
Span( | |
from u in numberPrefix.Maybe() | |
from w in digit.Many(1).Select(_ => new String(_.ToArray())) | |
from x in fraction.Maybe().Select(_ => _.Otherwise(() => "")) | |
from y in exponent.Maybe().Select(_ => _.Otherwise(() => "")) | |
from z in numberSuffix.Maybe().Select(_ => _.Otherwise(() => "")) | |
select u.Select(t => (YacqExpression) YacqExpression.Number(String.Concat(t, w, x, y, z))) | |
.Otherwise(() => (YacqExpression) YacqExpression.Number(String.Concat(w, x, y, z))), | |
(start, end, t) => t.SetPosition(start, end) | |
) | |
)); | |
} | |
// Lists | |
_standard.Add("term", "list", g => Span( | |
g["yacq", "expression"] | |
.Between(ignore, ignore) | |
.Many() | |
.Between('('.Satisfy(), ')'.Satisfy()) | |
.Select(es => (YacqExpression) YacqExpression.List(es)), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Vectors | |
_standard.Add("term", "vector", g => Span( | |
g["yacq", "expression"] | |
.Between(ignore, ignore) | |
.Many() | |
.Between('['.Satisfy(), ']'.Satisfy()) | |
.Select(es => (YacqExpression) YacqExpression.Vector(es)), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Lambda Lists | |
_standard.Add("term", "lambdaList", g => Span( | |
g["yacq", "expression"] | |
.Between(ignore, ignore) | |
.Many() | |
.Between('{'.Satisfy(), '}'.Satisfy()) | |
.Select(es => (YacqExpression) YacqExpression.LambdaList(es)), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Quotes | |
_standard.Add("term", "quote", g => Span( | |
Prims.Pipe( | |
Chars.Sequence("#'"), | |
g["yacq", "expression"], | |
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("quote"), e) | |
), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Quasiquotes | |
_standard.Add("term", "quasiquote", g => Span( | |
Prims.Pipe( | |
Chars.Sequence("#`"), | |
g["yacq", "expression"], | |
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("quasiquote"), e) | |
), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Unquote-Splicings | |
_standard.Add("term", "unquoteSplicing", g => Span( | |
Prims.Pipe( | |
Chars.Sequence("#,@"), | |
g["yacq", "expression"], | |
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("unquote-splicing"), e) | |
), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Unquotes | |
_standard.Add("term", "unquote", g => Span( | |
Prims.Pipe( | |
Chars.Sequence("#,"), | |
g["yacq", "expression"], | |
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("unquote"), e) | |
), | |
(start, end, t) => t.SetPosition(start, end) | |
)); | |
// Identifiers | |
_standard.Add("term", "identifier", g => Combinator.Choice( | |
Span('.'.Satisfy() | |
.Many(1) | |
.Select(cs => (YacqExpression) YacqExpression.Identifier(new String(cs.ToArray()))), | |
(start, end, t) => t.SetPosition(start, end) | |
), | |
Span(':'.Satisfy() | |
.Many(1) | |
.Select(cs => (YacqExpression) YacqExpression.Identifier(new String(cs.ToArray()))), | |
(start, end, t) => t.SetPosition(start, end) | |
), | |
Span(Chars.Digit() | |
.Not() | |
.Right(Chars.Space() | |
.Or(punctuation) | |
.Not() | |
.Right(Chars.Any()) | |
.Many(1) | |
) | |
.Select(cs => (YacqExpression) YacqExpression.Identifier(new String(cs.ToArray()))), | |
(start, end, t) => t.SetPosition(start, end) | |
) | |
)); | |
// Terms | |
_standard.Add("yacq", "term", g => Combinator.Choice(g["term"]).Between(ignore, ignore)); | |
// Infix Dots | |
_standard.Add("infix", "dot", g => Prims.Pipe( | |
g["yacq", "term"], | |
'.'.Satisfy() | |
.Right(g["yacq", "term"]) | |
.Many(), | |
(h, t) => t.Aggregate(h, (l, r) => | |
(YacqExpression) YacqExpression.List(YacqExpression.Identifier("."), l, r) | |
) | |
)); | |
// Infix Colons | |
_standard.Add("infix", "colon", g => Prims.Pipe( | |
g["infix", "dot"], | |
':'.Satisfy() | |
.Right(g["infix", "dot"]) | |
.Many(), | |
(h, t) => t.Aggregate(h, (l, r) => | |
(YacqExpression) YacqExpression.List(YacqExpression.Identifier(":"), l, r) | |
) | |
)); | |
expressionRef = _standard.Get["infix"].Last(); | |
_standard.Set.Default = g => g["yacq", "expression"]; | |
} | |
private static Parser<Char, T> Span<T>(Parser<Char, T> parser, | |
Action<Position, Position, T> action) | |
{ | |
if (parser == null) | |
{ | |
throw new ArgumentNullException("parser"); | |
} | |
if (action == null) | |
{ | |
throw new ArgumentNullException("action"); | |
} | |
Parser<Char, Position> pos = stream => Reply.Success(stream, stream.Position); | |
return from x in pos | |
from y in parser | |
from z in pos | |
select y.Apply(_ => action(x, z, _)); | |
} | |
// add... | |
// original quote has priority 600. | |
Grammar.Standard.Add("term", 550, "quote2", g => Span( | |
Prims.Pipe( | |
Chars.Sequence("#?"), | |
g["yacq", "expression"], | |
(p, e) => (YacqExpression) YacqExpression.List(YacqExpression.Identifier("quote"), e) | |
), | |
(start, end, t) => t.SetPosition(start, end) | |
)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment