ola-parser 1.0.1

Ola Language Parser
Documentation
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464

use super::program::*;
use lalrpop_util::ErrorRecovery;
grammar<'err>(file_no: usize, parser_errors: &'err mut Vec<ErrorRecovery<usize, Token<'input>, &'static str>>);

pub SourceUnit: SourceUnit = {
    SourceUnitPart* => SourceUnit(<>)
}

SourceUnitPart: SourceUnitPart = {
    ContractDefinition => SourceUnitPart::ContractDefinition(<>),
    ImportDirective => <>,
}

ImportDirective: SourceUnitPart = {
    <l:@L> "import" <s:StringLiteral> <r:@R> ";" => SourceUnitPart::ImportDirective(Import::Plain(s, Loc::File(file_no, l, r))),
    <l:@L> "import" <s:StringLiteral> "as" <id:Identifier> <r:@R> ";" => SourceUnitPart::ImportDirective(Import::GlobalSymbol(s, id, Loc::File(file_no, l, r))),
}

Type: Type = {
    "bool" => Type::Bool,
    "u32" => Type::Uint(32),
    "u64" => Type::Uint(64),
    "u256" => Type::Uint(256),
    "address" => Type::Address,
    "field" => Type::Field,
    "hash" => Type::Hash,
    "fields" => Type::DynamicBytes,
    "string" => Type::String,
        <l:@L> "mapping" "(" <k:Precedence0> <key_name:Identifier?> "=>" <v:Precedence0> <value_name:Identifier?> ")" <r:@R> => {
        Type::Mapping {
            loc: Loc::File(file_no, l, r),
            key: Box::new(k),
            key_name,
            value: Box::new(v),
            value_name }
    },

}

StorageLocation: StorageLocation = {
    <l:@L> "memory" <r:@R> => StorageLocation::Memory(Loc::File(file_no, l, r)),
    <l:@L> "storage" <r:@R> => StorageLocation::Storage(Loc::File(file_no, l, r)),
}



IdentifierOrError: Option<Identifier> = {
    Identifier => Some(<>),
    ! => {
        parser_errors.push(<>);
        None
    }
}

VariableDeclaration: VariableDeclaration = {
    <l:@L> <ty:Precedence0> <storage:StorageLocation?> <name:IdentifierOrError> <r:@R> => VariableDeclaration {
        loc: Loc::File(file_no, l, r), ty, storage, name
    },
}
StructDefinition: Box<StructDefinition> = {
    <l:@L> "struct" <name:IdentifierOrError> "{" <fields:(<VariableDeclaration> ";")*> "}" <r:@R> => {
        Box::new(StructDefinition{loc: Loc::File(file_no, l, r), name, fields})
    }
}

ContractTy: ContractTy = {
    <l:@L> "contract" <r:@R> => ContractTy::Contract(Loc::File(file_no, l, r)),
    <l:@L> "interface" <r:@R> => ContractTy::Interface(Loc::File(file_no, l, r)),
    <l:@L> "library" <r:@R> => ContractTy::Library(Loc::File(file_no, l, r)),
}

ContractPart: ContractPart = {
    StructDefinition => ContractPart::StructDefinition(<>),
    EnumDefinition => ContractPart::EnumDefinition(<>),
    VariableDefinition => ContractPart::VariableDefinition(<>),
    FunctionDefinition => ContractPart::FunctionDefinition(<>),
    TypeDefinition => ContractPart::TypeDefinition(<>),
    <l:@L> ";" <r:@R> => ContractPart::StraySemicolon(Loc::File(file_no, l, r)),
}

ContractDefinition: Box<ContractDefinition> = {
    <l:@L> <ty:ContractTy> <name:IdentifierOrError>
    "{" <parts:(<ContractPart>)*> "}" <r:@R> => {
        Box::new(ContractDefinition{loc: Loc::File(file_no, l, r), ty, name,  parts})
    }
}


EnumDefinition: Box<EnumDefinition> = {
    <l:@L> "enum" <name:IdentifierOrError> "{" <values:Comma<IdentifierOrError>> "}" <r:@R> => {
        Box::new(EnumDefinition{loc: Loc::File(file_no, l, r), name, values})
    }
}

VariableDefinition: Box<VariableDefinition> = {
    <l:@L> <ty:Precedence0>  <attrs:VariableAttribute*> <name:IdentifierOrError> <e:("=" <Expression>)?> <r:@R> ";" => {
        Box::new(VariableDefinition{
            loc: Loc::File(file_no, l, r), ty, attrs, name, initializer: e,
        })
    },
    <l:@L> <ty:Precedence0> <attrs:VariableAttribute*> <name:Identifier> <false_token:!>  <r:@R> ";" => {
        parser_errors.push (false_token);
        Box::new(VariableDefinition{
            loc: Loc::File(file_no, l, r), ty, attrs, name: Some(name), initializer: None,
        })
    },
}

TypeDefinition: Box<TypeDefinition> = {
    <l:@L> "type" <name:Identifier> "=" <ty:Precedence0> <r:@R> ";" => {
        Box::new(TypeDefinition{
            loc: Loc::File(file_no, l, r), name, ty
        })
    },
}

VariableAttribute: VariableAttribute = {
    <l:@L> "const" <r:@R> => VariableAttribute::Constant(Loc::File(file_no, l, r)),
    <l:@L> "mut" <r:@R> => VariableAttribute::Mutable(Loc::File(file_no, l, r)),
}

Expression: Expression = {
    Precedence14,
}

Precedence14: Expression = {
    <a:@L> <l:Precedence13> "=" <r:Precedence14> <b:@R> => Expression::Assign(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "|=" <r:Precedence14> <b:@R> => Expression::AssignOr(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "^=" <r:Precedence14> <b:@R> => Expression::AssignXor(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "&=" <r:Precedence14> <b:@R> => Expression::AssignAnd(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "<<=" <r:Precedence14> <b:@R> => Expression::AssignShiftLeft(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> ">>=" <r:Precedence14> <b:@R> => Expression::AssignShiftRight(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "+=" <r:Precedence14> <b:@R> => Expression::AssignAdd(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "-=" <r:Precedence14> <b:@R> => Expression::AssignSubtract(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "*=" <r:Precedence14> <b:@R> => Expression::AssignMultiply(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "/=" <r:Precedence14> <b:@R> => Expression::AssignDivide(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence13> "%=" <r:Precedence14> <b:@R> => Expression::AssignModulo(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <c:Precedence13> "?" <l:Precedence14> ":" <r:Precedence14> <b:@R> => {
        Expression::ConditionalOperator(Loc::File(file_no, a, b), Box::new(c), Box::new(l), Box::new(r))
    },
    Precedence13,
}

Precedence13: Expression = {
    <a:@L> <l:Precedence13> "||" <r:Precedence12> <b:@R> => Expression::Or(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence12,
}

Precedence12: Expression = {
    <a:@L> <l:Precedence12> "&&" <r:Precedence11> <b:@R> => Expression::And(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence11,
}

Precedence11: Expression = {
    <a:@L> <l:Precedence11> "==" <r:Precedence10> <b:@R> => Expression::Equal(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence11> "!=" <r:Precedence10> <b:@R> => Expression::NotEqual(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence10,
}

Precedence10: Expression = {
    <a:@L> <l:Precedence10> "<" <r:Precedence9> <b:@R> => Expression::Less(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence10> ">" <r:Precedence9> <b:@R> => Expression::More(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence10> "<=" <r:Precedence9> <b:@R> => Expression::LessEqual(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence10> ">=" <r:Precedence9> <b:@R> => Expression::MoreEqual(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence9,
}

Precedence9: Expression = {
    <a:@L> <l:Precedence9> "|" <r:Precedence8> <b:@R> => Expression::BitwiseOr(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence8,
}

Precedence8: Expression = {
    <a:@L> <l:Precedence8> "^" <r:Precedence7> <b:@R> => Expression::BitwiseXor(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence7,
}

Precedence7: Expression = {
    <a:@L> <l:Precedence7> "&" <r:Precedence6> <b:@R> => Expression::BitwiseAnd(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence6,
}

Precedence6: Expression = {
    <a:@L> <l:Precedence6> "<<" <r:Precedence5> <b:@R> => Expression::ShiftLeft(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence6> ">>" <r:Precedence5> <b:@R> => Expression::ShiftRight(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence5,
}

Precedence5: Expression = {
    <a:@L> <l:Precedence5> "+" <r:Precedence4> <b:@R> => Expression::Add(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence5> "-" <r:Precedence4> <b:@R> => Expression::Subtract(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence4,
}

Precedence4: Expression = {
    <a:@L> <l:Precedence4> "*" <r:Precedence3> <b:@R> => Expression::Multiply(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence4> "/" <r:Precedence3> <b:@R> => Expression::Divide(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    <a:@L> <l:Precedence4> "%" <r:Precedence3> <b:@R> => Expression::Modulo(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence3,
}

Precedence3: Expression = {
    <a:@L> <l:Precedence2> "**" <r:Precedence3> <b:@R> => Expression::Power(Loc::File(file_no, a, b), Box::new(l), Box::new(r)),
    Precedence2,
}

Precedence2: Expression = {
    <a:@L> "!" <e:Precedence2> <b:@R> => Expression::Not(Loc::File(file_no, a, b), Box::new(e)),
    <a:@L> "~" <e:Precedence2> <b:@R> => Expression::BitwiseNot(Loc::File(file_no, a, b), Box::new(e)),
    <a:@L> "delete" <e:Precedence2> <b:@R>  => Expression::Delete(Loc::File(file_no, a, b), Box::new(e)),
    <a:@L> "new" <call:Precedence2> <b:@R> => Expression::New(Loc::File(file_no, a, b), Box::new(call)),
    Precedence0,
}

NamedArgument: NamedArgument = {
    <l:@L> <name:Identifier> ":" <expr:Expression> <r:@R> => {
        NamedArgument{ loc: Loc::File(file_no, l, r), name, expr }
    },
    <l:@L> "address" <ar:@R> ":" <expr:Expression> <r:@R> => {
        let name = Identifier { loc:  Loc::File(file_no, l, ar), name: "address".into() };

        NamedArgument{ loc: Loc::File(file_no, l, r), name, expr }
    }
}

FunctionCall: Expression = {
    <a:@L> <i:Precedence0> "(" <v:Comma<Expression>> ")" <b:@R> => {
        Expression::FunctionCall(Loc::File(file_no, a, b), Box::new(i), v)
    },
    <l:@L> <i:Precedence0> <block:BlockStatement> <r:@R> => {
        Expression::FunctionCallBlock(Loc::File(file_no, l, r), Box::new(i), Box::new(block))
    },
    <a:@L> <i:Precedence0> "(" "{" <v:Comma<NamedArgument>> "}" ")" <b:@R> => {
        Expression::NamedFunctionCall(Loc::File(file_no, a, b), Box::new(i), v)
    },
}

Precedence0: Expression = {
    <a:@L> <e:Precedence0> "++" <b:@R> => Expression::Increment(Loc::File(file_no, a, b), Box::new(e)),
    <a:@L> <e:Precedence0> "--" <b:@R> => Expression::Decrement(Loc::File(file_no, a, b), Box::new(e)),
    <FunctionCall> => <>,
    <a:@L> <e:Precedence0> "[" <i:Expression?> "]" <b:@R> => Expression::ArraySubscript(Loc::File(file_no, a, b), Box::new(e), i.map(Box::new)),
    <a:@L> <e:Precedence0> "[" <l:Expression?> ":" <r:Expression?> "]" <b:@R> => Expression::ArraySlice(Loc::File(file_no, a, b), Box::new(e), l.map(Box::new), r.map(Box::new)),
    <a:@L> <e:Precedence0> "." <i:Identifier> <b:@R> => Expression::MemberAccess(Loc::File(file_no, a, b), Box::new(e), i),
    <l:@L> <ty:Type> <r:@R> => Expression::Type(Loc::File(file_no, l, r), ty),
    <a:@L> "[" <v:CommaOne<Expression>> "]" <b:@R> => {
        Expression::ArrayLiteral(Loc::File(file_no, a, b), v)
    },
    <Identifier> => Expression::Variable(<>),
    <l:@L> <a:ParameterList> <r:@R> => {
        if a.len() == 1 {
            if let Some(Parameter{ ty, name: None, .. }) = &a[0].1 {
                // this means "(" Expression ")"
                return Expression::Parenthesis(ty.loc(), Box::new(ty.clone()));
            }
        }

        Expression::List(Loc::File(file_no, l, r), a)
    },
    LiteralExpression
}

LiteralExpression: Expression = {
    <a:@L> "true" <b:@R> => Expression::BoolLiteral(Loc::File(file_no, a, b), true),
    <a:@L> "false" <b:@R> => Expression::BoolLiteral(Loc::File(file_no, a, b), false),
    <l:@L> <n: Number> <r:@R> =>  {
        Expression::NumberLiteral(Loc::File(file_no, l, r), n)
      },
    <l:@L> <n: FieldsLiteral> <r:@R> =>  {
        Expression::FieldsLiteral(Loc::File(file_no, l, r), n.chars().skip(2).filter(|c|  *c != '"' && *c != '\'').collect())
    },
    <l:@L> <n: HexNumber> <r:@R> =>  {
        Expression::HexNumberLiteral(Loc::File(file_no, l, r), n.chars().skip(2).filter(|c|  *c != '"' && *c != '\'').collect())
    },
    <StringLiteral+> => Expression::StringLiteral(<>),
    <l:@L> <a: AddressLiteral> <r:@R> => {
        Expression::AddressLiteral(Loc::File(file_no, l, r), a.chars().skip(2).filter(|c|  *c != '"' && *c != '\'').collect())
    },
    <l:@L> <a: HashLiteral> <r:@R> => {
        Expression::HashLiteral(Loc::File(file_no, l, r), a.chars().skip(2).filter(|c|  *c != '"' && *c != '\'').collect())
    },
}


// A parameter list is used for function arguments, returns, and destructuring statements.
// In destructuring statements, parameters can be optional. So, we make parameters optional
// and as an added bonus we can generate error messages about missing parameters/returns
// to functions
Parameter: Parameter = {
    <l:@L> <ty:Expression> <name:Identifier?> <r:@R> => {
        let loc = Loc::File(file_no, l, r);
        Parameter{loc, ty, name}
    }
}

OptParameter: (Loc, Option<Parameter>) = {
    <l:@L> <p:Parameter?> <r:@R> => (Loc::File(file_no, l, r), p),
}

ParameterList: Vec<(Loc, Option<Parameter>)> = {
    "(" ")" => Vec::new(),
    "(" <l:@L> <p:Parameter> <r:@R> ")" => vec!((Loc::File(file_no, l, r), Some(p))),
    "(" <CommaTwo<OptParameter>> ")" => <>,
    "(" <false_token:!> ")"=> {
        parser_errors.push(<>);
        Vec::new()
    }
}
BlockStatementOrSemiColon: Option<Statement> = {
    <@L> <@R> ";" => None,
    BlockStatement => Some(<>),
}
FunctionDefinition: Box<FunctionDefinition> = {
    <l:@L> "fn" <nl:@L> <name: IdentifierOrError> <nr:@R> <params:ParameterList>
    <returns: ("->" ParameterList)?> <r:@R> <body:BlockStatementOrSemiColon> => {
        let (_, returns) = returns.unwrap_or(("", Vec::new()));
        Box::new(FunctionDefinition{
            loc: Loc::File(file_no, l, r),
            name,
            name_loc: Loc::File(file_no, nl, nr),
            params,
            returns,
            body,
        })
    },
}


BlockStatement: Statement = {
    <l:@L> "{" <statements:Statement*> "}" <r:@R> => {
        Statement::Block { loc: Loc::File(file_no, l, r), statements }
    },
    <l:@L> "{" <v:CommaOne<NamedArgument>> "}" <r:@R> => Statement::Args(Loc::File(file_no, l, r), v),
}

OpenStatement: Statement = {
    <l:@L> "if" "(" <cond:Expression> ")" <body:Statement> <r:@R> => {
        Statement::If(Loc::File(file_no, l, r), cond, Box::new(body), None)
    },
    <l:@L> "if" "(" <cond:Expression> ")" <body:ClosedStatement> "else" <o:OpenStatement> <r:@R> => {
        Statement::If(Loc::File(file_no, l, r), cond, Box::new(body), Some(Box::new(o)))
    },
    <l:@L> "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Expression?> ")" <block:OpenStatement> <r:@R> => {
        Statement::For(Loc::File(file_no, l, r), b.map(Box::new), c.map(Box::new), n.map(Box::new), Some(Box::new(block)))
    },
    <l:@L> "while" "(" <e:Expression> ")" <b:OpenStatement> <r:@R> => {
        Statement::While(Loc::File(file_no, l, r), e, Box::new(b))
    }
}

ClosedStatement: Statement = {
    NonIfStatement,
    <l:@L> "if" "(" <cond:Expression> ")" <body:ClosedStatement> "else" <o:ClosedStatement> <r:@R> => {
        Statement::If(Loc::File(file_no, l, r), cond, Box::new(body), Some(Box::new(o)))
    },
    <l:@L> "while" "(" <e:Expression> ")" <b:ClosedStatement> <r:@R> => {
        Statement::While(Loc::File(file_no, l, r), e, Box::new(b))
    },
    <l:@L> "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Expression?> ")" <block:ClosedStatement> <r:@R> => {
        Statement::For(Loc::File(file_no, l, r), b.map(Box::new), c.map(Box::new), n.map(Box::new), Some(Box::new(block)))
    },
    <l:@L> "for" "(" <b:SimpleStatement?> ";" <c:Expression?> ";" <n:Expression?> ")" <r:@R> ";" => {
        Statement::For(Loc::File(file_no, l, r), b.map(Box::new), c.map(Box::new), n.map(Box::new), None)
    }
}

Statement: Statement = {
    OpenStatement,
    ClosedStatement,
    <l:@L> <false_token:!> <r:@R> => {
        parser_errors.push(false_token);
        Statement::Error(Loc::File(file_no, l, r))
    },
}

SimpleStatement: Statement = {
    <l:@L> <v:VariableDeclaration> <e:("=" <Expression>)?> <r:@R> => {
        Statement::VariableDefinition(Loc::File(file_no, l, r), v, e)
    },
    <l:@L> <e:Expression> <r:@R> => {
        Statement::Expression(Loc::File(file_no, l, r), e)
    }
}

NonIfStatement: Statement = {
    BlockStatement => <>,
    <SimpleStatement> ";" => <>,
    <l:@L> "do" <b:Statement> "while" "(" <e:Expression> ")" <r:@R> ";" => {
        Statement::DoWhile(Loc::File(file_no, l, r), Box::new(b), e)
    },
    <l:@L> "continue" <r:@R> ";" => {
        Statement::Continue(Loc::File(file_no, l, r))
    },
    <l:@L> "break" <r:@R> ";" => {
        Statement::Break(Loc::File(file_no, l, r))
    },
    <l:@L> "return" <r:@R> ";" => {
        Statement::Return(Loc::File(file_no, l, r), None)
    },
    <l:@L> "return" <e:Expression> <r:@R> ";" => {
        Statement::Return(Loc::File(file_no, l, r), Some(e))
    },
}


Comma<T>: Vec<T> = {
    => Vec::new(),
    CommaOne<T> => <>,
};

CommaOne<T>: Vec<T> = {
    <e:T> <v:("," <T>)*> => {
        let mut v = v;
        v.insert(0, e);
        v
    }
};

CommaTwo<T>: Vec<T> = {
    <e:T> <v:("," <T>)+> => {
        let mut v = v;
        v.insert(0, e);
        v
    }
};

Identifier: Identifier = {
    <l:@L> <s: r#"[$_]*[a-zA-Z][a-zA-Z$_0-9]*"#> <r:@R> => Identifier{loc: Loc::File(file_no, l, r), name: s.to_string()}
}

Number: String = {
    r#"([1-9][0-9]*|0)"# => <>.to_string(),
}

FieldsLiteral: String = {
    r#"0x[0-9A-Fa-f]+fields"# => <>.to_string(),
}

HexNumber: String = {
    r#"0x[0-9A-Fa-f]+"# => <>.to_string()
}

AddressLiteral: String = {
    r#"0x([0-9A-Fa-f]{64})address"# => <>.to_string(),
}

HashLiteral: String = {
    r#"0x[0-9A-Fa-f]{64}hash"# => <>.to_string(),
}


StringLiteral: StringLiteral = {
    <l:@L> <s:r#""[^"]*""#> <r:@R> => {
        StringLiteral{ loc: Loc::File(file_no, l, r), string: s[1..s.len()-1].to_string()}
    }
}

match {
    r"\s*" => {},
    r"//[^\n\r]*[\n\r]" => {},
    r"/\*[^*]*\*+(?:[^/*][^*]*\*+)*/" => { },
    _,
}