What's new? | Help | Directory | Sign in
Google
boo-extensions
Useful extensions, macros and attributes to the boo programming language
  
  
  
  
    
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
170
171
172
173
174
175
176
177
178
179
180
namespace Boo.Pegs

import Boo.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 UnaryExpression(Operator: UnaryOperatorType.Increment, Operand: expression):
return [| one_or_many($(expand(expression))) |]

case UnaryExpression(Operator: UnaryOperatorType.Decrement, Operand: expression):
return [| zero_or_many($(expand(expression))) |]

case UnaryExpression(Operator: UnaryOperatorType.OnesComplement, Operand: expression):
return [| choice($(expand(expression)), empty()) |]

case UnaryExpression(Operator: UnaryOperatorType.LogicalNot, Operand: expression):
return [| not_predicate($(expand(expression))) |]

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

case BinaryExpression(
Operator: BinaryOperatorType.Division,
Left: l,
Right: r):

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

case BinaryExpression(
Operator: BinaryOperatorType.BitwiseAnd,
Left: l,
Right: r):

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

case BinaryExpression(
Operator: BinaryOperatorType.Subtraction,
Left: l,
Right: 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

r71 by rodrigobamboo on May 17, 2008   Diff
better test case for last match rule and
last match support inside actions
Go to: 
Project members, sign in to write a code review

Older revisions

r66 by rodrigobamboo on May 08, 2008   Diff
support for rebinding rules
r64 by rodrigobamboo on May 08, 2008   Diff
use / as the prioritized choice
operator to look closer to the
original paper;
allow digits in char range
expressions;
r57 by rodrigobamboo on May 07, 2008   Diff
better support for debugging with rule
names and PegDebugContext
All revisions of this file

File info

Size: 5175 bytes, 180 lines