My favorites | Sign in
Project Logo
                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
namespace Boo.Pegs

import Boo.Lang.PatternMatching
import Boo.Lang.Compiler
import Boo.Lang.Compiler.Ast

def expand(e as Expression) as Expression:
match e:
case ArrayLiteralExpression(Items: items):
return expandArguments([| sequence() |], items)

case ListLiteralExpression(Items: items):
return expandArguments([| choice() |], items)

case [| ++ $expression |]:
return [| one_or_many($(expand(expression))) |]

case [| -- $expression |]:
return [| zero_or_many($(expand(expression))) |]

case [| ~ $expression |]:
return [| choice($(expand(expression)), empty()) |]

case [| not $expression |]:
return [| not_predicate($(expand(expression))) |]

case s=StringLiteralExpression():
return [| terminal($s) |]

case [| $l / $r |]:
return [| choice($(expand(l)), $(expand(r))) |]

case [| $l & $r |]:
return [| predict($(expand(l)), $(expand(r))) |]

case [| $l - $r |]:
return [| char_range($(charFor(l)), $(charFor(r))) |]

case block=BlockExpression():
template = [| { context as PegContext | _ } |]
block.Parameters = template.Parameters
return ClosureExpander().Expand([| action($block) |])

case MemberReferenceExpression():
return e

case reference=ReferenceExpression(Name: name):
if name.StartsWith("@"):
reference.Name = name[1:]
return [| same_match($reference) |]
return reference

otherwise:
return e

class ClosureExpander(DepthFirstTransformer):
def Expand(node as Expression):
return self.VisitNode(node)

override def LeaveSpliceExpression(node as SpliceExpression):
match node.Expression:
case r=ReferenceExpression():
mie = node.ParentNode as MethodInvocationExpression
if mie is not null and mie.Target is node:
mie.Arguments.Insert(0, [| context |])
ReplaceCurrentNode(node.Expression)
return

ReplaceCurrentNode([| $r(context) |])

override def OnReferenceExpression(node as ReferenceExpression):
if not node.Name.StartsWith("@"): return

node.Name = node.Name[1:]
ReplaceCurrentNode([| context.RuleState.LastMatchFor($node) |])

def charFor(e as Expression):
match e:
case r=ReferenceExpression():
return CharLiteralExpression(e.LexicalInfo, r.Name)
case i=IntegerLiteralExpression():
return CharLiteralExpression(e.LexicalInfo, cast(char, cast(int, char('0')) + i.Value))

def expandArguments(invocation as MethodInvocationExpression, args):
for arg in args:
invocation.Arguments.Add(expand(arg))
return invocation

macro peg:
/*"""
Usage:

peg MyGrammar:

// sequence
MyRule1 = E1, E2, EN

// choice
MyRule2 = [Choice1, Choice2, ChoiceN]
MyRule3 = Choice1 / Choice2

// repetition (one or many)
MyRule4 = ++E

// repetition (zero or many)
MyRule5 = --E

// optional
MyRule6 = ~OptionalPrefix, Suffix

// semantic action
MyRule7 = { print $text }

Example:

peg miniboo:
Module = Spacing, ++Class, EndOfFile
Class = CLASS, Identifier, Begin, ++Member, End
Member = DEF, Identifier, LPAREN, RPAREN, Block
Block = Begin, ++Statement, End
Statement = Invocation
Invocation = ++Expression
Expression = Identifier / String
String = "'", ++(not "'", any()), "'", Spacing
Identifier = ++[a-z, A-Z], OptionalSpacing
Begin = ":", Spacing
End = empty()
Spacing = ++[' ', '\t', '\r', '\n']
OptionalSpacing = ~Spacing
CLASS = "class", Spacing
DEF = "def", Spacing
LPAREN = "(", OptionalSpacing
RPAREN = ")", OptionalSpacing
EndOfFile = not any()
"""*/

rules = []
for node in peg.Block.Statements:
match node:
case ExpressionStatement(
Expression: BinaryExpression(
Operator: BinaryOperatorType.Assign,
Left: rule = ReferenceExpression(),
Right: expression)):

rules.Add((rule, expression))

result = Block()

# declare all rules
for rule as ReferenceExpression, _ in rules:
if rule.NodeType != NodeType.ReferenceExpression: continue
decl = DeclarationStatement(
Declaration(Name: rule.Name, Type: SimpleTypeReference("PegRule")),
[| PegRule($(rule.Name)) |])
result.Add(decl)

# expand all the expressions
for rule as ReferenceExpression, expression as Expression in rules:
try:
expansion = expand(expression)
if rule.NodeType == NodeType.ReferenceExpression:
result.Add([| $rule.Expression = $expansion |])
else:
result.Add([| $rule = $expansion |])
except x:
Context.Errors.Add(CompilerErrorFactory.MacroExpansionError(rule, x))

return result
Show details Hide details

Change log

r219 by rodrigobamboo on Dec 25, 2008   Diff
Boo.PatternMatching =>
Boo.Lang.PatternMatching
Go to: 
Project members, sign in to write a code review

Older revisions

r93 by rodrigobamboo on Jul 26, 2008   Diff
code pattern matching
r71 by rodrigobamboo on May 17, 2008   Diff
better test case for last match rule
and last match support inside actions
r66 by rodrigobamboo on May 08, 2008   Diff
support for rebinding rules
All revisions of this file

File info

Size: 4706 bytes, 169 lines
Hosted by Google Code