/*----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //-----------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------- // Dafny // Rustan Leino, first created 25 January 2008 //--------------------------------------------------------------------------*/ using System.Collections.Generic; using Microsoft.Boogie; COMPILER Dafny /*--------------------------------------------------------------------------*/ static List! theClasses = new List(); static Expression! dummyExpr = new LiteralExpr(Token.NoToken); static Statement! dummyStmt = new ReturnStmt(Token.NoToken); static Attributes.Argument! dummyAttrArg = new Attributes.Argument("dummyAttrArg"); static Scope! parseVarScope = new Scope(); // helper routine for parsing call statements private static void RecordCallLhs(IdentifierExpr! e, List! lhs, List! newVars) { lhs.Add(e); if (parseVarScope.Find(e.Name) == null) { VarDecl d = new VarDecl(e.tok, e.Name, new InferredTypeProxy(), null); newVars.Add(d); parseVarScope.Push(e.Name, e.Name); } } // helper routine for parsing call statements private static Expression! ConvertToLocal(Expression! e) { FieldSelectExpr fse = e as FieldSelectExpr; if (fse != null && fse.Obj is ImplicitThisExpr) { return new IdentifierExpr(fse.tok, fse.FieldName); } return e; // cannot convert to IdentifierExpr (or is already an IdentifierExpr) } /// /// Parses top level declarations from "filename" and appends them to "classes". /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. /// public static int Parse (string! filename, List! classes) /* throws System.IO.IOException */ { using (System.IO.StreamReader reader = new System.IO.StreamReader(filename)) { BoogiePL.Buffer.Fill(reader); Scanner.Init(filename); return Parse(classes); } } /// /// Parses top level declarations and appends them to "classes". /// Returns the number of parsing errors encountered. /// Note: first initialize the Scanner. /// public static int Parse (List! classes) { List oldClasses = theClasses; theClasses = classes; Parse(); theClasses = oldClasses; return Errors.count; } /*--------------------------------------------------------------------------*/ CHARACTERS letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". digit = "0123456789". special = "'_?`\\". glyph = "`~!@#$%^&*()-_=+[{]}|;:',<.>/?\\". cr = '\r'. lf = '\n'. tab = '\t'. space = ' '. quote = '"'. nondigit = letter + special. nonquote = letter + digit + space + glyph. /*------------------------------------------------------------------------*/ TOKENS ident = nondigit {nondigit | digit}. digits = digit {digit}. string = quote {nonquote} quote. COMMENTS FROM "/*" TO "*/" NESTED COMMENTS FROM "//" TO lf IGNORE cr + lf + tab /*------------------------------------------------------------------------*/ PRODUCTIONS Dafny = (. ClassDecl! c; .) { ClassDecl (. theClasses.Add(c); .) } EOF . ClassDecl = (. Token! id; Attributes attrs = null; List typeArgs = new List(); List members = new List(); .) "class" { Attribute } Ident [ GenericParameters ] "{" { ClassMemberDecl } "}" (. c = new ClassDecl(id, id.val, typeArgs, members, attrs); .) . ClassMemberDecl! mm> = (. Method! m; Function! f; .) ( FieldDecl | FunctionDecl (. mm.Add(f); .) | MethodDecl (. mm.Add(m); .) | FrameDecl ) . FieldDecl! mm> = (. Attributes attrs = null; Token! id; Type! ty; .) "var" { Attribute } IdentType (. mm.Add(new Field(id, id.val, ty, attrs)); .) { "," IdentType (. mm.Add(new Field(id, id.val, ty, attrs)); .) } ";" . IdentType = Ident ":" Type . IdentTypeOptional = (. Token! id; Type! ty; Type optType = null; .) Ident [ ":" Type (. optType = ty; .) ] (. var = new BoundVar(id, id.val, optType == null ? new InferredTypeProxy() : optType); .) . /*------------------------------------------------------------------------*/ GenericParameters! typeArgs> = (. Token! id; .) "<" Ident (. typeArgs.Add(new TypeParameter(id, id.val)); .) { "," Ident (. typeArgs.Add(new TypeParameter(id, id.val)); .) } ">" . FrameDecl = (. Token! id; Attributes attrs = null; .) "frame" { Attribute } Ident "{" /* TBD */ "}" . /*------------------------------------------------------------------------*/ MethodDecl = (. Token! id; Attributes attrs = null; List! typeArgs = new List(); List ins = new List(); List outs = new List(); List req = new List(); List mod = new List(); List ens = new List(); Statement! bb; Statement body = null; .) "method" { Attribute } Ident [ GenericParameters ] (. parseVarScope.PushMarker(); .) Formals [ "returns" Formals ] ( ";" { MethodSpec } | { MethodSpec } BlockStmt (. body = bb; .) ) (. parseVarScope.PopMarker(); m = new Method(id, id.val, typeArgs, ins, outs, req, mod, ens, body, attrs); .) . MethodSpec! req, List! mod, List! ens> = (. Expression! e; bool isFree = false; .) ( "modifies" [ Expression (. mod.Add(e); .) { "," Expression (. mod.Add(e); .) } ] ";" | [ "free" (. isFree = true; .) ] ( "requires" Expression ";" (. req.Add(new MaybeFreeExpression(e, isFree)); .) | "ensures" Expression ";" (. ens.Add(new MaybeFreeExpression(e, isFree)); .) ) ) . Formals! formals> = (. Token! id; Type! ty; .) "(" [ IdentType (. formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val); .) { "," IdentType (. formals.Add(new Formal(id, id.val, ty, incoming)); parseVarScope.Push(id.val, id.val); .) } ] ")" . /*------------------------------------------------------------------------*/ Type = (. ty = new BoolType(); /*keep compiler happy*/ .) ( "bool" (. /* yeah, that's it! */ .) | "int" (. ty = new IntType(); .) | ReferenceType ) . ReferenceType = (. Token! x; ty = new BoolType(); /*keep compiler happy*/ List! gt; .) ( "object" (. ty = new ObjectType(); .) | (. gt = new List(); .) Ident [ GenericInstantiation ] (. ty = new ClassType(x, x.val, gt); .) | (. gt = new List(); .) "set" GenericInstantiation (. if (gt.Count != 1) { SemErr("set type expects exactly one type argument"); } ty = new SetType(gt[0]); .) | (. gt = new List(); .) "seq" GenericInstantiation (. if (gt.Count != 1) { SemErr("seq type expects exactly one type argument"); } ty = new SeqType(gt[0]); .) ) . GenericInstantiation! gt> = (. Type! ty; .) "<" Type (. gt.Add(ty); .) { "," Type (. gt.Add(ty); .) } ">" . /*------------------------------------------------------------------------*/ FunctionDecl = (. Attributes attrs = null; Token! id; List typeArgs = new List(); List formals = new List(); Type! returnType; List reqs = new List(); List reads = new List(); Expression! bb; Expression body = null; bool use = false; .) "function" { Attribute } [ "use" (. use = true; .) ] Ident [ GenericParameters ] (. parseVarScope.PushMarker(); .) Formals ":" Type ( ";" { FunctionSpec } | { FunctionSpec } ExtendedExpr (. body = bb; .) ) (. parseVarScope.PopMarker(); f = new Function(id, id.val, use, typeArgs, formals, returnType, reqs, reads, body, attrs); .) . FunctionSpec! reqs, List! reads> = (. Expression! e; .) ( "requires" Expression ";" (. reqs.Add(e); .) | ReadsClause ) . ReadsClause! reads> = "reads" [ Expressions ] ";" . ExtendedExpr = (. e = dummyExpr; .) "{" ( IfThenElseExpr | Expression ) "}" . IfThenElseExpr = (. Token! x; Expression! e0; Expression! e1 = dummyExpr; .) "if" (. x = token; .) "(" Expression ")" ExtendedExpr "else" ( IfThenElseExpr | ExtendedExpr ) (. e = new ITEExpr(x, e, e0, e1); .) . /*------------------------------------------------------------------------*/ BlockStmt = (. Token! x; List body = new List(); Statement! s; .) (. parseVarScope.PushMarker(); .) "{" (. x = token; .) { Stmt } "}" (. block = new BlockStmt(x, body); .) (. parseVarScope.PopMarker(); .) . Stmt! ss> = (. Statement! s; .) /* By first reading a sequence of block statements, we avoid problems in the generated parser, despite the ambiguity in the grammar. See Note in ConstAtomExpression production. */ { BlockStmt (. ss.Add(s); .) } ( OneStmt (. ss.Add(s); .) | VarDeclStmts ) . OneStmt = (. Token! x; Token! id; string label = null; s = dummyStmt; /* to please the compiler */ .) /* This list does not contain BlockStmt, see comment above in Stmt production. */ ( AssertStmt | AssumeStmt | UseStmt | AssignStmt | HavocStmt | CallStmt | IfStmt | WhileStmt | ForeachStmt | "label" (. x = token; .) Ident ":" (. s = new LabelStmt(x, id.val); .) | "break" (. x = token; .) [ Ident (. label = id.val; .) ] ";" (. s = new BreakStmt(x, label); .) | "return" (. x = token; .) ";" (. s = new ReturnStmt(x); .) ) . AssignStmt = (. Token! x; Expression! lhs; Expression rhs; Type ty; s = dummyStmt; .) LhsExpr ":=" (. x = token; .) AssignRhs (. if (rhs != null) { s = new AssignStmt(x, lhs, rhs); } else { assert ty != null; s = new AssignStmt(x, lhs, ty); } .) ";" . AssignRhs /* ensures e == null <==> ty == null; */ = (. Expression! ee; Type! tt; e = null; ty = null; .) ( "new" ReferenceType (. ty = tt; .) | Expression (. e = ee; .) ) (. if (e == null && ty == null) { e = dummyExpr; } .) . HavocStmt = (. Token! x; Expression! lhs; .) "havoc" (. x = token; .) LhsExpr ";" (. s = new AssignStmt(x, lhs); .) . LhsExpr = Expression /* TODO: restrict LHS further */ . VarDeclStmts! ss> = (. VarDecl! d; .) "var" IdentTypeRhs (. ss.Add(d); parseVarScope.Push(d.Name, d.Name); .) { "," IdentTypeRhs (. ss.Add(d); parseVarScope.Push(d.Name, d.Name); .) } ";" . IdentTypeRhs = (. Token! id; Type! ty; Expression! e; Expression rhs = null; Type newType = null; Type optionalType = null; DeterminedAssignmentRhs optionalRhs = null; .) Ident [ ":" Type (. optionalType = ty; .) ] [ ":=" AssignRhs ] (. if (rhs != null) { assert newType == null; optionalRhs = new ExprRhs(rhs); } else if (newType != null) { optionalRhs = new TypeRhs(newType); } else if (optionalType == null) { optionalType = new InferredTypeProxy(); } d = new VarDecl(id, id.val, optionalType, optionalRhs); .) . IfStmt = (. Token! x; Expression guard; Statement! thn; Statement! s; Statement els = null; .) "if" (. x = token; .) Guard BlockStmt [ "else" ( IfStmt (. els = s; .) | BlockStmt (. els = s; .) ) ] (. ifStmt = new IfStmt(x, guard, thn, els); .) . WhileStmt = (. Token! x; Expression guard; bool isFree; Expression! e; List invariants = new List(); List decreases = new List(); Statement! body; .) "while" (. x = token; .) Guard (. assume guard == null || Owner.None(guard); .) {( (. isFree = false; .) [ "free" (. isFree = true; .) ] "invariant" Expression (. invariants.Add(new MaybeFreeExpression(e, isFree)); .) ";" ) | ( "decreases" Expression (. decreases.Add(e); .) { "," Expression (. decreases.Add(e); .) } ";" ) } BlockStmt (. stmt = new WhileStmt(x, guard, invariants, decreases, body); .) . Guard /* null represents demonic-choice */ = (. Expression! ee; e = null; .) "(" ( "*" (. e = null; .) | Expression (. e = ee; .) ) ")" . CallStmt = (. Token! x, id; Expression! e; List lhs = new List(); List newVars = new List(); .) "call" (. x = token; .) CallStmtSubExpr [ "," /* call a,b,c,... := ... */ (. e = ConvertToLocal(e); if (e is IdentifierExpr) { RecordCallLhs((IdentifierExpr)e, lhs, newVars); } else if (e is FieldSelectExpr) { SemErr(e.tok, "each LHS of call statement must be a variable, not a field"); } else { SemErr(e.tok, "each LHS of call statement must be a variable"); } .) Ident (. RecordCallLhs(new IdentifierExpr(id, id.val), lhs, newVars); .) { "," Ident (. RecordCallLhs(new IdentifierExpr(id, id.val), lhs, newVars); .) } ":=" CallStmtSubExpr | ":=" /* call a := ... */ (. e = ConvertToLocal(e); if (e is IdentifierExpr) { RecordCallLhs((IdentifierExpr)e, lhs, newVars); } else if (e is FieldSelectExpr) { SemErr(e.tok, "each LHS of call statement must be a variable, not a field"); } else { SemErr(e.tok, "each LHS of call statement must be a variable"); } .) CallStmtSubExpr ] ";" /* "e" has now been parsed as one of: IdentifierExpr, FunctionCallExpr, FieldSelectExpr. It denotes the RHS, so to be legal it must be a FunctionCallExpr. */ (. if (e is FunctionCallExpr) { FunctionCallExpr fce = (FunctionCallExpr)e; s = new CallStmt(x, newVars, lhs, fce.Receiver, fce.Name, fce.Args); // this actually does an ownership transfer of fce.Args } else { SemErr("RHS of call statement must denote a method invocation"); s = new CallStmt(x, newVars, lhs, dummyExpr, "dummyMethodName", new List()); } .) . /*------------------------------------------------------------------------*/ ForeachStmt = (. Token! x, boundVar; Type! ty; Expression! collection; Expression! range; List bodyPrefix = new List(); AssignStmt bodyAssign = null; .) (. parseVarScope.PushMarker(); .) "foreach" (. x = token; range = new LiteralExpr(x, true); ty = new InferredTypeProxy(); .) "(" Ident [ ":" Type ] "in" Expression (. parseVarScope.Push(boundVar.val, boundVar.val); .) [ "|" Expression ] ")" "{" { AssertStmt (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .) | AssumeStmt (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .) | UseStmt (. if (s is PredicateStmt) { bodyPrefix.Add((PredicateStmt)s); } .) } ( AssignStmt (. if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } .) | HavocStmt (. if (s is AssignStmt) { bodyAssign = (AssignStmt)s; } .) ) "}" (. s = new ForeachStmt(x, new BoundVar(boundVar, boundVar.val, ty), collection, range, bodyPrefix, bodyAssign); .) (. parseVarScope.PopMarker(); .) . AssertStmt = (. Token! x; Expression! e; .) "assert" (. x = token; .) Expression ";" (. s = new AssertStmt(x, e); .) . AssumeStmt = (. Token! x; Expression! e; .) "assume" (. x = token; .) Expression ";" (. s = new AssumeStmt(x, e); .) . UseStmt = (. Token! x; Expression! e; .) "use" (. x = token; .) Expression ";" (. s = new UseStmt(x, e); .) . /*------------------------------------------------------------------------*/ Expression = (. Token! x; Expression! e1; .) ImpliesExpression { EquivOp (. x = token; .) ImpliesExpression (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Iff, e0, e1); .) } . EquivOp = "<==>" | '\u21d4'. /*------------------------------------------------------------------------*/ ImpliesExpression = (. Token! x; Expression! e1; .) LogicalExpression [ ImpliesOp (. x = token; .) ImpliesExpression (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Imp, e0, e1); .) ] . ImpliesOp = "==>" | '\u21d2'. /*------------------------------------------------------------------------*/ LogicalExpression = (. Token! x; Expression! e1; .) RelationalExpression [ AndOp (. x = token; .) RelationalExpression (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .) { AndOp (. x = token; .) RelationalExpression (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.And, e0, e1); .) } | OrOp (. x = token; .) RelationalExpression (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .) { OrOp (. x = token; .) RelationalExpression (. e0 = new BinaryExpr(x, BinaryExpr.Opcode.Or, e0, e1); .) } ] . AndOp = "&&" | '\u2227'. OrOp = "||" | '\u2228'. /*------------------------------------------------------------------------*/ RelationalExpression = (. Token! x; Expression! e1; BinaryExpr.Opcode op; .) Term [ RelOp Term (. e0 = new BinaryExpr(x, op, e0, e1); .) ] . RelOp = (. x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/; .) ( "==" (. x = token; op = BinaryExpr.Opcode.Eq; .) | "<" (. x = token; op = BinaryExpr.Opcode.Lt; .) | ">" (. x = token; op = BinaryExpr.Opcode.Gt; .) | "<=" (. x = token; op = BinaryExpr.Opcode.Le; .) | ">=" (. x = token; op = BinaryExpr.Opcode.Ge; .) | "!=" (. x = token; op = BinaryExpr.Opcode.Neq; .) | "!!" (. x = token; op = BinaryExpr.Opcode.Disjoint; .) | "in" (. x = token; op = BinaryExpr.Opcode.In; .) | "!in" (. x = token; op = BinaryExpr.Opcode.NotIn; .) | '\u2260' (. x = token; op = BinaryExpr.Opcode.Neq; .) | '\u2264' (. x = token; op = BinaryExpr.Opcode.Le; .) | '\u2265' (. x = token; op = BinaryExpr.Opcode.Ge; .) ) . /*------------------------------------------------------------------------*/ Term = (. Token! x; Expression! e1; BinaryExpr.Opcode op; .) Factor { AddOp Factor (. e0 = new BinaryExpr(x, op, e0, e1); .) } . AddOp = (. x = Token.NoToken; op=BinaryExpr.Opcode.Add/*(dummy)*/; .) ( "+" (. x = token; op = BinaryExpr.Opcode.Add; .) | "-" (. x = token; op = BinaryExpr.Opcode.Sub; .) ) . /*------------------------------------------------------------------------*/ Factor = (. Token! x; Expression! e1; BinaryExpr.Opcode op; .) UnaryExpression { MulOp UnaryExpression (. e0 = new BinaryExpr(x, op, e0, e1); .) } . MulOp = (. x = Token.NoToken; op = BinaryExpr.Opcode.Add/*(dummy)*/; .) ( "*" (. x = token; op = BinaryExpr.Opcode.Mul; .) | "/" (. x = token; op = BinaryExpr.Opcode.Div; .) | "%" (. x = token; op = BinaryExpr.Opcode.Mod; .) ) . /*------------------------------------------------------------------------*/ UnaryExpression = (. Token! x; e = dummyExpr; .) ( "-" (. x = token; .) UnaryExpression (. e = new BinaryExpr(x, BinaryExpr.Opcode.Sub, new LiteralExpr(x, 0), e); .) | NegOp (. x = token; .) UnaryExpression (. e = new UnaryExpr(x, UnaryExpr.Opcode.Not, e); .) | SelectExpression | ConstAtomExpression ) . NegOp = "!" | '\u00ac'. ConstAtomExpression = (. Token! x; int n; List! elements; e = dummyExpr; .) ( "false" (. e = new LiteralExpr(token, false); .) | "true" (. e = new LiteralExpr(token, true); .) | "null" (. e = new LiteralExpr(token); .) | Nat (. e = new LiteralExpr(token, n); .) | "fresh" (. x = token; .) "(" Expression ")" (. e = new FreshExpr(x, e); .) | "|" (. x = token; .) Expression (. e = new UnaryExpr(x, UnaryExpr.Opcode.SeqLength, e); .) "|" /* Note, the following open-curly-brace causes grammar ambiguities that result in two Coco warnings. One of these is the confusion between BlockStmt and AssignStmt, the former starting with an open curly brace and the latter starting with a left-hand side Expression, which syntactically can start with an open curly brace. The other is the confusion between a quantifier's triggers/attributes and the quantifier's body. The disamiguation I've chosen involves giving priority to BlockStmt (since no semantically legal AssignStmt can have a set constructor as its left-hand side) and to triggers/attributes (which, unfortunately, changes what programs are syntactically allowed, but there is a simple workaround, which is for a user to put parentheses around any quantifier body that begins with a set constructor. */ | "{" (. x = token; elements = new List(); .) [ Expressions ] (. e = new SetDisplayExpr(x, elements); .) "}" | "[" (. x = token; elements = new List(); .) [ Expressions ] (. e = new SeqDisplayExpr(x, elements); .) "]" ) . /*------------------------------------------------------------------------*/ /* returns one of: -- IdentifierExpr -- FunctionCallExpr -- FieldSelectExpr */ CallStmtSubExpr = (. e = dummyExpr; .) ( IdentOrFuncExpression | ObjectExpression SelectOrCallSuffix ) { SelectOrCallSuffix } . SelectExpression = (. Token! id; e = dummyExpr; .) ( IdentOrFuncExpression | ObjectExpression ) { SelectOrCallSuffix } . IdentOrFuncExpression = (. Token! id; e = dummyExpr; List! args; .) Ident [ "(" (. args = new List(); .) [ Expressions ] ")" (. e = new FunctionCallExpr(id, id.val, new ImplicitThisExpr(id), args); .) ] (. if (e == dummyExpr) { if (parseVarScope.Find(id.val) != null) { e = new IdentifierExpr(id, id.val); } else { e = new FieldSelectExpr(id, new ImplicitThisExpr(id), id.val); } } .) . SelectOrCallSuffix = (. Token! id, x; List! args; Expression e0 = null; Expression e1 = null; Expression! ee; bool anyDots = false; bool func = false; .) ( "." Ident [ "(" (. args = new List(); func = true; .) [ Expressions ] ")" (. e = new FunctionCallExpr(id, id.val, e, args); .) ] (. if (!func) { e = new FieldSelectExpr(id, e, id.val); } .) | "[" (. x = token; .) ( Expression (. e0 = ee; .) [ ".." (. anyDots = true; .) [ Expression (. e1 = ee; .) ] | ":=" Expression (. e1 = ee; .) ] | ".." Expression (. anyDots = true; e1 = ee; .) ) (. assert !anyDots ==> e0 != null; if (anyDots) { assert e0 != null || e1 != null; e = new SeqSelectExpr(x, false, e, e0, e1); } else if (e1 == null) { assert e0 != null; e = new SeqSelectExpr(x, true, e, e0, null); } else { assert e0 != null; e = new SeqUpdateExpr(x, e, e0, e1); } .) "]" ) . /* ObjectExpression represents those expressions E that could possibly be used in E.f or E(...), except Ident. Since the lookahead is just 1, quantifier expressions are also parsed here. The expression returned is never an lvalue. */ ObjectExpression = (. Token! x; e = dummyExpr; .) ( "this" (. e = new ThisExpr(token); .) | "old" (. x = token; .) "(" Expression ")" (. e = new OldExpr(x, e); .) | "(" ( QuantifierGuts | Expression ) ")" ) . /*------------------------------------------------------------------------*/ QuantifierGuts = (. Token! x = Token.NoToken; bool univ = false; BoundVar! bv; List bvars = new List(); Token! tok; Expr! e; ExprSeq! es; Attributes attrs = null; Triggers trigs = null; Expression! body; .) ( Forall (. x = token; univ = true; .) | Exists (. x = token; .) ) (. parseVarScope.PushMarker(); .) IdentTypeOptional (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .) { "," IdentTypeOptional (. bvars.Add(bv); parseVarScope.Push(bv.Name, bv.Name); .) } QSep /* The grammar is ambiguous in how to resolve triggers/attributes versus the expression body. However, the loop generated by Coco for the next two lines of grammar declarations is still fine--it will loop, picking up as many triggers/attributes it can before going on to parse an expression. This seems good, because there's a simple workaround for quantifier bodies that begin with a set constructor: simply put parentheses around the quantifier body. See also the Note in production ConstAtomExpression. */ { AttributeOrTrigger } Expression (. if (univ) { q = new ForallExpr(x, bvars, body, trigs, attrs); } else { q = new ExistsExpr(x, bvars, body, trigs, attrs); } parseVarScope.PopMarker(); .) . Forall = "forall" | '\u2200'. Exists = "exists" | '\u2203'. QSep = "::" | '\u2022'. Expressions! args> = (. Expression! e; .) Expression (. args.Add(e); .) { "," Expression (. args.Add(e); .) } . /*------------------------------------------------------------------------*/ Attribute = "{" AttributeBody "}" . AttributeBody = (. string aName; List aArgs = new List(); Attributes.Argument! aArg; .) ":" ident (. aName = token.val; .) [ AttributeArg (. aArgs.Add(aArg); .) { "," AttributeArg (. aArgs.Add(aArg); .) } ] (. attrs = new Attributes(aName, aArgs, attrs); .) . AttributeArg = (. Expression! e; arg = dummyAttrArg; .) ( string (. arg = new Attributes.Argument(token.val.Substring(1, token.val.Length-2)); .) | Expression (. arg = new Attributes.Argument(e); .) ) . AttributeOrTrigger = (. List es = new List(); .) "{" ( AttributeBody | (. es = new List(); .) Expressions (. trigs = new Triggers(es, trigs); .) ) "}" . /*------------------------------------------------------------------------*/ Ident = ident (. x = token; .) . Nat = digits (. try { n = System.Convert.ToInt32(token.val); } catch (System.FormatException) { SemErr("incorrectly formatted number"); n = 0; } .) . END Dafny.