protected override void Write(CiBinaryExpr expr) { switch (expr.Op) { case CiToken.Equal: case CiToken.NotEqual: if (expr.Left.Type is CiStringType && !expr.Left.IsConst(null) && !expr.Right.IsConst(null)) { if (expr.Op == CiToken.NotEqual) { Write('!'); } Write(expr.Left); Write(".equals("); Write(expr.Right); Write(')'); return; } break; default: break; } base.Write(expr); }
protected override void WriteEqual(CiBinaryExpr expr, CiPriority parent, bool not) { CiType coercedType; if (IsPtrTo(expr.Left, expr.Right)) { coercedType = expr.Left.Type; } else if (IsPtrTo(expr.Right, expr.Left)) { coercedType = expr.Right.Type; } else { base.WriteEqual(expr, parent, not); return; } if (parent > CiPriority.Equality) { Write('('); } WriteCoerced(coercedType, expr.Left, CiPriority.Equality); Write(GetEqOp(not)); WriteCoerced(coercedType, expr.Right, CiPriority.Equality); if (parent > CiPriority.Equality) { Write(')'); } }
public override CiExpr Visit(CiBinaryExpr expr, CiPriority parent) { switch (expr.Op) { case CiToken.AndAssign: case CiToken.OrAssign: case CiToken.XorAssign: if (parent > CiPriority.Assign) { Write('('); } expr.Left.Accept(this, CiPriority.Assign); Write(' '); Write(expr.OpString); Write(' '); WriteAssignRight(expr); if (parent > CiPriority.Assign) { Write(')'); } return(expr); default: return(base.Visit(expr, parent)); } }
public override CiExpr Visit(CiBinaryExpr expr, CiPriority parent) { switch (expr.Op) { case CiToken.Slash when expr.Type is CiIntegerType: bool floorDiv; if (expr.Left is CiRangeType leftRange && leftRange.Min >= 0 && expr.Right is CiRangeType rightRange && rightRange.Min >= 0) { if (parent > CiPriority.Or) { Write('('); } floorDiv = true; } else { Write("int("); floorDiv = false; } expr.Left.Accept(this, CiPriority.Mul); Write(floorDiv ? " // " : " / "); expr.Right.Accept(this, CiPriority.Primary); if (!floorDiv || parent > CiPriority.Or) { Write(')'); } return(expr);
protected virtual void Write(CiBinaryExpr expr) { WriteChild(expr, expr.Left); switch (expr.Op) { case CiToken.Plus: case CiToken.Asterisk: case CiToken.Less: case CiToken.LessOrEqual: case CiToken.Greater: case CiToken.GreaterOrEqual: case CiToken.Equal: case CiToken.NotEqual: case CiToken.And: case CiToken.Or: case CiToken.Xor: case CiToken.CondAnd: case CiToken.CondOr: WriteOp(expr); WriteChild(expr, expr.Right); break; case CiToken.Minus: case CiToken.Slash: case CiToken.Mod: case CiToken.ShiftLeft: case CiToken.ShiftRight: WriteOp(expr); WriteNonAssocChild(expr, expr.Right); break; default: throw new ArgumentException(expr.Op.ToString()); } }
protected override void WriteCharAt(CiBinaryExpr expr) { expr.Left.Accept(this, CiPriority.Primary); Write(".charCodeAt("); expr.Right.Accept(this, CiPriority.Statement); Write(')'); }
protected override void WriteEqual(CiBinaryExpr expr, CiPriority parent, bool not) { if ((expr.Left.Type is CiStringType && expr.Right.Type != CiSystem.NullType) || (expr.Right.Type is CiStringType && expr.Left.Type != CiSystem.NullType)) { if (not) { Write('!'); } expr.Left.Accept(this, CiPriority.Primary); Write(".equals("); expr.Right.Accept(this, CiPriority.Statement); Write(')'); } else if (expr.Left is CiBinaryExpr leftBinary && leftBinary.Op == CiToken.LeftBracket && IsUnsignedByte(leftBinary.Type) && expr.Right is CiLiteral rightLiteral && rightLiteral.Value is long l && l >= 0 && l <= byte.MaxValue) { if (parent > CiPriority.Equality) { Write('('); } base.WriteIndexing(leftBinary, CiPriority.Equality); // omit "& 0xff" Write(not ? " != " : " == "); Write((sbyte)l); if (parent > CiPriority.Equality) { Write(')'); } }
protected override void WriteEqual(CiBinaryExpr expr, CiPriority parent, bool not) { string op = IsPtr(expr.Left) || IsPtr(expr.Right) ? not ? " is not " : " is " : not ? " != " : " == "; WriteComparison(expr, parent, CiPriority.Equality, op); }
protected static bool IsStringEmpty(CiBinaryExpr expr, out CiExpr str) { if (expr.Left is CiSymbolReference symbol && symbol.Symbol == CiSystem.StringLength && expr.Right is CiLiteral literal && (long)literal.Value == 0) { str = symbol.Left; return(true); } str = null; return(false); }
protected static CiExpr IsTrimSubstring(CiBinaryExpr expr) { if (IsStringSubstring(expr.Right, out bool cast, out CiExpr ptr, out CiExpr offset, out CiExpr length) && !cast && expr.Left is CiSymbolReference leftSymbol && ptr.IsReferenceTo(leftSymbol.Symbol) && // TODO: more complex expr offset.IsLiteralZero) { return(length); } return(null); }
protected override void Write(CiBinaryExpr expr) { switch (expr.Op) { case CiToken.Equal: case CiToken.NotEqual: if (expr.Left.IsConst(null)) { // null != thing -> defined(thing) // null == thing -> !defined(thing) if (expr.Op == CiToken.Equal) { Write('!'); } WriteDefined(expr.Right); } else if (expr.Right.IsConst(null)) { // thing != null -> defined(thing) // thing == null -> !defined(thing) if (expr.Op == CiToken.Equal) { Write('!'); } WriteDefined(expr.Left); } else if (expr.Left.Type is CiStringType) { WriteChild(expr, expr.Left); Write(expr.Op == CiToken.Equal ? " eq " : " ne "); WriteChild(expr, expr.Right); } else { base.Write(expr); } break; #if !USE_INTEGER case CiToken.Slash: Write("int("); WriteChild(3, expr.Left); Write(" / "); WriteNonAssocChild(3, expr.Right); Write(')'); break; #endif default: base.Write(expr); break; } }
protected override void Write(CiBinaryExpr expr) { if (expr.Op == CiToken.Slash) { Write("int("); WriteChild(3, expr.Left); Write(" / "); WriteNonAssocChild(3, expr.Right); Write(')'); } else { base.Write(expr); } }
protected override void WriteComparison(CiBinaryExpr expr, CiPriority parent, CiPriority child, string op) { if (expr.Left.IsIndexing && expr.Left is CiBinaryExpr indexing && indexing.Left.Type is CiStringType && expr.Right is CiLiteral literal && literal.Value is long c && IsAscii(c)) { if (parent > child) { Write('('); } expr.Left.Accept(this, child); Write(op); WriteCharLiteral((char)c); if (parent > child) { Write(')'); } }
protected void WriteOp(CiBinaryExpr expr) { switch (expr.Op) { case CiToken.Plus: Write(" + "); break; case CiToken.Minus: Write(" - "); return; case CiToken.Asterisk: Write(" * "); break; case CiToken.Slash: Write(" / "); break; case CiToken.Mod: Write(" % "); break; case CiToken.ShiftLeft: Write(" << "); break; case CiToken.ShiftRight: Write(" >> "); break; case CiToken.Less: Write(" < "); break; case CiToken.LessOrEqual: Write(" <= "); break; case CiToken.Greater: Write(" > "); break; case CiToken.GreaterOrEqual: Write(" >= "); break; case CiToken.Equal: Write(" == "); break; case CiToken.NotEqual: Write(" != "); break; case CiToken.And: Write(" & "); break; case CiToken.Or: Write(" | "); break; case CiToken.Xor: Write(" ^ "); break; case CiToken.CondAnd: Write(" && "); break; case CiToken.CondOr: Write(" || "); break; default: throw new ArgumentException(expr.Op.ToString()); } }
public override CiExpr Visit(CiBinaryExpr expr, CiPriority parent) { if (expr.Op == CiToken.Slash && expr.Type is CiIntegerType) { if (parent > CiPriority.Or) { Write('('); } expr.Left.Accept(this, CiPriority.Mul); Write(" / "); expr.Right.Accept(this, CiPriority.Primary); Write(" | 0"); // FIXME: long: Math.trunc? if (parent > CiPriority.Or) { Write(')'); } return(expr); } return(base.Visit(expr, parent)); }
protected override void WriteEqual(CiBinaryExpr expr, CiPriority parent, bool not) { if (NeedStringPtrData(expr.Left) && expr.Right.Type == CiSystem.NullType) { WriteCoerced(CiSystem.StringPtrType, expr.Left, CiPriority.Primary); Write(".data()"); Write(GetEqOp(not)); Write("nullptr"); } else if (expr.Left.Type == CiSystem.NullType && NeedStringPtrData(expr.Right)) { Write("nullptr"); Write(GetEqOp(not)); WriteCoerced(CiSystem.StringPtrType, expr.Right, CiPriority.Primary); Write(".data() "); } else { base.WriteEqual(expr, parent, not); } }
CiExpr ParseMulExpr() { CiExpr left = ParsePrimaryExpr(); for (;;) { switch (this.CurrentToken) { case CiToken.Asterisk: case CiToken.Slash: case CiToken.Mod: left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParsePrimaryExpr() }; break; default: return(left); } } }
protected override void WriteEqual(CiBinaryExpr expr, CiPriority parent, bool not) { if ((expr.Left.Type is CiStringType && expr.Right.Type != CiSystem.NullType) || (expr.Right.Type is CiStringType && expr.Left.Type != CiSystem.NullType)) { this.StringEquals = true; if (not) { Write('!'); } Write("streq("); expr.Left.Accept(this, CiPriority.Statement); Write(", "); expr.Right.Accept(this, CiPriority.Statement); Write(')'); } else { base.WriteEqual(expr, parent, not); } }
protected override void Write(CiBinaryExpr expr) { switch (expr.Op) { case CiToken.Equal: case CiToken.NotEqual: case CiToken.Greater: if (expr.Left.Type is CiStringType && !expr.Left.IsConst(null) && !expr.Right.IsConst(null)) { Write("strcmp("); Write(expr.Left); Write(", "); Write(expr.Right); Write(')'); WriteOp(expr); Write('0'); break; } // optimize str.Length == 0, str.Length != 0, str.Length > 0 CiPropertyAccess pa = expr.Left as CiPropertyAccess; if (pa != null && pa.Property == CiLibrary.StringLengthProperty) { CiConstExpr ce = expr.Right as CiConstExpr; if (ce != null && 0.Equals(ce.Value)) { WriteChild(CiPriority.Postfix, pa.Obj); Write(expr.Op == CiToken.Equal ? "[0] == '\\0'" : "[0] != '\\0'"); break; } } base.Write(expr); break; case CiToken.ShiftLeft: case CiToken.ShiftRight: WriteChildWithSuggestedParentheses(expr, expr.Left, CiPriority.Multiplicative, true); WriteOp(expr); WriteChildWithSuggestedParentheses(expr, expr.Right, CiPriority.Multiplicative, false); break; case CiToken.And: case CiToken.Or: case CiToken.Xor: WriteChildWithSuggestedParentheses(expr, expr.Left, CiPriority.Multiplicative, true); WriteOp(expr); WriteChildWithSuggestedParentheses(expr, expr.Right, CiPriority.Multiplicative, true); break; case CiToken.CondOr: WriteChildWithSuggestedParentheses(expr, expr.Left, CiPriority.Or, true); Write(" || "); WriteChildWithSuggestedParentheses(expr, expr.Right, CiPriority.Or, true); break; default: base.Write(expr); break; } }
protected abstract void WriteForRange(CiVar iter, CiBinaryExpr cond, long rangeStep);
protected override void WriteCharAt(CiBinaryExpr expr) { Write("ord("); WriteIndexing(expr, CiPriority.Statement); Write(')'); }
void WriteChildWithSuggestedParentheses(CiBinaryExpr parent, CiExpr child, CiPriority suggestedParentPriority, bool assoc) { if (assoc && GetPriority(parent) == GetPriority(child)) Write(child); else WriteChild(suggestedParentPriority, child); }
protected override void WriteCharAt(CiBinaryExpr expr) { WriteCall(expr.Left, "charCodeAt", expr.Right); }
CiExpr ParsePrimaryExpr() { CiExpr result; switch (this.CurrentToken) { case CiToken.Increment: case CiToken.Decrement: case CiToken.Minus: case CiToken.Tilde: case CiToken.ExclamationMark: case CiToken.New: return(new CiPrefixExpr { Line = this.Line, Op = NextToken(), Inner = ParsePrimaryExpr() }); case CiToken.Literal: result = new CiLiteral(this.CurrentValue) { Line = this.Line }; NextToken(); break; case CiToken.InterpolatedString: result = ParseInterpolatedString(); break; case CiToken.LeftParenthesis: Expect(CiToken.LeftParenthesis); result = ParseType(); Expect(CiToken.RightParenthesis); break; case CiToken.Id: result = ParseSymbolReference(null); break; case CiToken.List: result = ParseListType(); break; case CiToken.Resource: NextToken(); Expect(CiToken.Less); if (ParseId() != "byte") { throw ParseException("Expected resource<byte[]>"); } Expect(CiToken.LeftBracket); Expect(CiToken.RightBracket); Expect(CiToken.Greater); result = new CiPrefixExpr { Line = this.Line, Op = CiToken.Resource, Inner = ParseParenthesized() }; break; default: throw ParseException("Invalid expression"); } for (;;) { switch (this.CurrentToken) { case CiToken.Dot: NextToken(); result = ParseSymbolReference(result); break; case CiToken.LeftParenthesis: result = new CiBinaryExpr { Line = this.Line, Left = result, Op = NextToken(), Right = ParseCollection(CiToken.RightParenthesis) }; break; case CiToken.LeftBracket: result = new CiBinaryExpr { Line = this.Line, Left = result, Op = NextToken(), Right = See(CiToken.RightBracket) ? null : ParseExpr() }; Expect(CiToken.RightBracket); break; case CiToken.Increment: case CiToken.Decrement: case CiToken.ExclamationMark: case CiToken.Hash: result = new CiPostfixExpr { Line = this.Line, Inner = result, Op = NextToken() }; break; default: return(result); } } }
CiExpr ParseMulExpr() { CiExpr left = ParsePrimaryExpr(); while (See(CiToken.Asterisk) || See(CiToken.Slash) || See(CiToken.Mod)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBinaryExpr { Left = left, Op = op, Right = ParsePrimaryExpr() }; } return left; }
public abstract CiExpr Visit(CiBinaryExpr expr, CiPriority parent);
CiExpr ParseAddExpr() { CiExpr left = ParseMulExpr(); while (See(CiToken.Plus) || See(CiToken.Minus)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBinaryExpr { Left = left, Op = op, Right = ParseMulExpr() }; } return left; }
CiExpr ParseAndExpr() { CiExpr left = ParseEqualityExpr(); while (Eat(CiToken.And)) left = new CiBinaryExpr { Left = left, Op = CiToken.And, Right = ParseEqualityExpr() }; return left; }
CiExpr ICiExprVisitor.Visit(CiBinaryExpr expr) { CiExpr left = Resolve(expr.Left); CiExpr right = Resolve(expr.Right); if (expr.Op == CiToken.Plus && (left.Type is CiStringType || right.Type is CiStringType)) { if (!(left is CiConstExpr && right is CiConstExpr)) throw new ResolveException("String concatenation allowed only for constants. Consider using +="); string a = GetConstString(left); string b = GetConstString(right); return new CiConstExpr(a + b); } left = Coerce(left, CiIntType.Value); right = Coerce(right, CiIntType.Value); if (right is CiConstExpr) { int b = GetConstInt(right); if (left is CiConstExpr) { int a = GetConstInt(left); switch (expr.Op) { case CiToken.Asterisk: a *= b; break; case CiToken.Slash: a /= b; break; case CiToken.Mod: a %= b; break; case CiToken.And: a &= b; break; case CiToken.ShiftLeft: a <<= b; break; case CiToken.ShiftRight: a >>= b; break; case CiToken.Plus: a += b; break; case CiToken.Minus: a -= b; break; case CiToken.Or: a |= b; break; case CiToken.Xor: a ^= b; break; } return new CiConstExpr(a); } if (expr.Op == CiToken.And && (b & ~0xff) == 0) { CiCoercion c = left as CiCoercion; if (c != null && c.Inner.Type == CiByteType.Value) left = (CiExpr) c.Inner; } } expr.Left = left; expr.Right = right; return expr; }
protected override void Write(CiBinaryExpr expr) { switch (expr.Op) { case CiToken.Equal: case CiToken.NotEqual: if (expr.Left.IsConst(null)) { // null != thing -> defined(thing) // null == thing -> !defined(thing) if (expr.Op == CiToken.Equal) Write('!'); WriteDefined(expr.Right); } else if (expr.Right.IsConst(null)) { // thing != null -> defined(thing) // thing == null -> !defined(thing) if (expr.Op == CiToken.Equal) Write('!'); WriteDefined(expr.Left); } else if (expr.Left.Type is CiStringType) { WriteChild(expr, expr.Left); Write(expr.Op == CiToken.Equal ? " eq " : " ne "); WriteChild(expr, expr.Right); } else base.Write(expr); break; #if !USE_INTEGER case CiToken.Slash: Write("int("); WriteChild(3, expr.Left); Write(" / "); WriteNonAssocChild(3, expr.Right); Write(')'); break; #endif default: base.Write(expr); break; } }
CiExpr ParseShiftExpr() { CiExpr left = ParseAddExpr(); while (See(CiToken.ShiftLeft) || See(CiToken.ShiftRight)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBinaryExpr { Left = left, Op = op, Right = ParseAddExpr() }; } return left; }
protected override void Write(CiMethodCall expr) { if (expr.Method == CiLibrary.MulDivMethod) { #if USE_INTEGER // FIXME: overflow on 32-bit perl Write("("); #else Write("int("); #endif WriteMulDiv(CiPriority.Multiplicative, expr); } else if (expr.Method == CiLibrary.CharAtMethod) { Write("ord(substr("); Write(expr.Obj); Write(", "); Write(expr.Arguments[0]); Write(", 1))"); } else if (expr.Method == CiLibrary.SubstringMethod) { Write("substr("); Write(expr.Obj); Write(", "); Write(expr.Arguments[0]); Write(", "); Write(expr.Arguments[1]); Write(')'); } else if (expr.Method == CiLibrary.ArrayCopyToMethod) { CiExpr lenMinus1 = new CiBinaryExpr { Left = expr.Arguments[3], Op = CiToken.Minus, Right = new CiConstExpr(1) }; WriteSlice(expr.Arguments[1], expr.Arguments[2], lenMinus1); Write(" = "); WriteSlice(expr.Obj, expr.Arguments[0], lenMinus1); } else if (expr.Method == CiLibrary.ArrayToStringMethod) { CiExpr lenMinus1 = new CiBinaryExpr { Left = expr.Arguments[1], Op = CiToken.Minus, Right = new CiConstExpr(1) }; Write("pack('U*', "); WriteSlice(expr.Obj, expr.Arguments[0], lenMinus1); Write(')'); } else if (expr.Method == CiLibrary.ArrayStorageClearMethod) { Write('@'); if (expr.Obj is CiVarAccess) Write(((CiVarAccess) expr.Obj).Var.Name); else { Write('{'); Write(expr.Obj); Write('}'); } Write(" = (0) x "); Write(((CiArrayStorageType) expr.Obj.Type).Length); } else { if (expr.Method != null) { if (expr.Obj != null) { Write(expr.Obj); Write("->"); } else { Write(this.Package); Write(expr.Method.Class.Name); Write("::"); } WriteLowercaseWithUnderscores(expr.Method.Name); } else { // delegate call Write(expr.Obj); Write("->"); } WriteArguments(expr); } }
CiExpr ParseXorExpr() { CiExpr left = ParseAndExpr(); while (Eat(CiToken.Xor)) left = new CiBinaryExpr { Left = left, Op = CiToken.Xor, Right = ParseAndExpr() }; return left; }
protected override void Write(CiBinaryExpr expr) { if (expr.Op == CiToken.Slash) { Write("int("); WriteChild(CiPriority.Multiplicative, expr.Left); Write(" / "); WriteNonAssocChild(CiPriority.Multiplicative, expr.Right); Write(')'); } else base.Write(expr); }
CiExpr ParseConstInitializer() { if (Eat(CiToken.LeftBrace)) { return new CiCollection { Line = this.Line, Items = ParseCollection(CiToken.RightBrace) } } ; return(ParseExpr()); } CiExpr[] ParseCollection(CiToken closing) { List <CiExpr> items = new List <CiExpr>(); if (!See(closing)) { do { items.Add(ParseExpr()); }while (Eat(CiToken.Comma)); } Expect(closing); return(items.ToArray()); } void CheckXcrementParent() { if (this.XcrementParent != null) { string op = this.CurrentToken == CiToken.Increment ? "++" : "--"; throw ParseException(op + " not allowed on the right side of " + this.XcrementParent); } } bool SeeDigit() { int c = PeekChar(); return(c >= '0' && c <= '9'); } CiInterpolatedString ParseInterpolatedString() { int line = this.Line; List <CiInterpolatedPart> parts = new List <CiInterpolatedPart>(); do { string prefix = (string)this.CurrentValue; NextToken(); CiExpr arg = ParseExpr(); CiExpr width = null; char format = ' '; int precision = -1; if (Eat(CiToken.Comma)) { width = ParseExpr(); } if (See(CiToken.Colon)) { format = (char)ReadChar(); if ("DdEeFfGgXx".IndexOf(format) < 0) { throw ParseException("Invalid format specifier"); } if (SeeDigit()) { precision = ReadChar() - '0'; if (SeeDigit()) { precision = precision * 10 + ReadChar() - '0'; } } NextToken(); } parts.Add(new CiInterpolatedPart(prefix, arg) { WidthExpr = width, Format = format, Precision = precision }); Check(CiToken.RightBrace); } while (ReadInterpolatedString() == CiToken.InterpolatedString); string suffix = (string)this.CurrentValue; NextToken(); return(new CiInterpolatedString(parts.ToArray(), suffix) { Line = line }); } CiExpr ParseParenthesized() { Expect(CiToken.LeftParenthesis); CiExpr result = ParseExpr(); Expect(CiToken.RightParenthesis); return(result); } CiExpr ParsePrimaryExpr() { CiExpr result; switch (this.CurrentToken) { case CiToken.Increment: case CiToken.Decrement: CheckXcrementParent(); goto case CiToken.Minus; case CiToken.Minus: case CiToken.Tilde: case CiToken.ExclamationMark: case CiToken.New: return(new CiPrefixExpr { Line = this.Line, Op = NextToken(), Inner = ParsePrimaryExpr() }); case CiToken.Literal: result = new CiLiteral(this.CurrentValue) { Line = this.Line }; NextToken(); break; case CiToken.InterpolatedString: result = ParseInterpolatedString(); break; case CiToken.LeftParenthesis: Expect(CiToken.LeftParenthesis); result = ParseType(); Expect(CiToken.RightParenthesis); break; case CiToken.Id: result = ParseSymbolReference(null); break; case CiToken.List: result = ParseListType(); break; case CiToken.Dictionary: result = ParseDictionaryType(CiSystem.DictionaryClass); break; case CiToken.SortedDictionary: result = ParseDictionaryType(CiSystem.SortedDictionaryClass); break; case CiToken.Resource: NextToken(); Expect(CiToken.Less); if (ParseId() != "byte") { throw ParseException("Expected resource<byte[]>"); } Expect(CiToken.LeftBracket); Expect(CiToken.RightBracket); Expect(CiToken.Greater); result = new CiPrefixExpr { Line = this.Line, Op = CiToken.Resource, Inner = ParseParenthesized() }; break; default: throw ParseException("Invalid expression"); } for (;;) { switch (this.CurrentToken) { case CiToken.Dot: NextToken(); result = ParseSymbolReference(result); break; case CiToken.LeftParenthesis: if (!(result is CiSymbolReference symbol)) { throw ParseException("Expected a method"); } NextToken(); result = new CiCallExpr { Line = this.Line, Method = symbol, Arguments = ParseCollection(CiToken.RightParenthesis) }; break; case CiToken.LeftBracket: result = new CiBinaryExpr { Line = this.Line, Left = result, Op = NextToken(), Right = See(CiToken.RightBracket) ? null : ParseExpr() }; Expect(CiToken.RightBracket); break; case CiToken.Increment: case CiToken.Decrement: CheckXcrementParent(); goto case CiToken.ExclamationMark; case CiToken.ExclamationMark: case CiToken.Hash: result = new CiPostfixExpr { Line = this.Line, Inner = result, Op = NextToken() }; break; default: return(result); } } } CiExpr ParseMulExpr() { CiExpr left = ParsePrimaryExpr(); for (;;) { switch (this.CurrentToken) { case CiToken.Asterisk: case CiToken.Slash: case CiToken.Mod: left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParsePrimaryExpr() }; break; default: return(left); } } } CiExpr ParseAddExpr() { CiExpr left = ParseMulExpr(); while (See(CiToken.Plus) || See(CiToken.Minus)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseMulExpr() } } ; return(left); } CiExpr ParseShiftExpr() { CiExpr left = ParseAddExpr(); while (See(CiToken.ShiftLeft) || See(CiToken.ShiftRight)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseAddExpr() } } ; return(left); } CiExpr ParseRelExpr() { CiExpr left = ParseShiftExpr(); for (;;) { switch (this.CurrentToken) { case CiToken.Less: case CiToken.LessOrEqual: case CiToken.Greater: case CiToken.GreaterOrEqual: left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseShiftExpr() }; break; default: return(left); } } } CiExpr ParseEqualityExpr() { CiExpr left = ParseRelExpr(); while (See(CiToken.Equal) || See(CiToken.NotEqual)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseRelExpr() } } ; return(left); } CiExpr ParseAndExpr() { CiExpr left = ParseEqualityExpr(); while (See(CiToken.And)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseEqualityExpr() } } ; return(left); } CiExpr ParseXorExpr() { CiExpr left = ParseAndExpr(); while (See(CiToken.Xor)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseAndExpr() } } ; return(left); } CiExpr ParseOrExpr() { CiExpr left = ParseXorExpr(); while (See(CiToken.Or)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseXorExpr() } } ; return(left); } CiExpr ParseCondAndExpr() { CiExpr left = ParseOrExpr(); while (See(CiToken.CondAnd)) { string saveXcrementParent = this.XcrementParent; this.XcrementParent = "&&"; left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseOrExpr() }; this.XcrementParent = saveXcrementParent; } return(left); } CiExpr ParseCondOrExpr() { CiExpr left = ParseCondAndExpr(); while (See(CiToken.CondOr)) { string saveXcrementParent = this.XcrementParent; this.XcrementParent = "||"; left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseCondAndExpr() }; this.XcrementParent = saveXcrementParent; } return(left); } CiExpr ParseExpr() { CiExpr left = ParseCondOrExpr(); if (See(CiToken.QuestionMark)) { CiSelectExpr result = new CiSelectExpr { Line = this.Line, Cond = left }; NextToken(); string saveXcrementParent = this.XcrementParent; this.XcrementParent = "?"; result.OnTrue = ParseExpr(); Expect(CiToken.Colon); result.OnFalse = ParseExpr(); this.XcrementParent = saveXcrementParent; return(result); } return(left); } CiExpr ParseType() { CiExpr left = ParseExpr(); if (Eat(CiToken.Range)) { return new CiBinaryExpr { Line = this.Line, Left = left, Op = CiToken.Range, Right = ParseExpr() } } ; return(left); } CiExpr ParseAssign(bool allowVar) { CiExpr left = ParseType(); switch (this.CurrentToken) { case CiToken.Assign: case CiToken.AddAssign: case CiToken.SubAssign: case CiToken.MulAssign: case CiToken.DivAssign: case CiToken.ModAssign: case CiToken.AndAssign: case CiToken.OrAssign: case CiToken.XorAssign: case CiToken.ShiftLeftAssign: case CiToken.ShiftRightAssign: return(new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseAssign(false) }); case CiToken.Id: if (allowVar) { return(ParseVar(left)); } return(left); default: return(left); } } CiVar ParseVar(CiExpr type) { CiVar def = new CiVar { Line = this.Line, TypeExpr = type, Name = ParseId() }; if (Eat(CiToken.Assign)) { def.Value = ParseAssign(false); } return(def); } CiVar ParseVar() { return(ParseVar(ParseType())); } CiConst ParseConst() { Expect(CiToken.Const); CiConst konst = new CiConst { Line = this.Line, TypeExpr = ParseType(), Name = ParseId() }; Expect(CiToken.Assign); konst.Value = ParseConstInitializer(); Expect(CiToken.Semicolon); return(konst); } CiBlock ParseBlock() { int line = this.Line; Expect(CiToken.LeftBrace); List <CiStatement> statements = new List <CiStatement>(); while (!Eat(CiToken.RightBrace)) { statements.Add(ParseStatement()); } return(new CiBlock { Line = line, Statements = statements.ToArray() }); } CiAssert ParseAssert() { CiAssert result = new CiAssert { Line = this.Line }; Expect(CiToken.Assert); result.Cond = ParseExpr(); if (Eat(CiToken.Comma)) { result.Message = ParseExpr(); } Expect(CiToken.Semicolon); return(result); } CiBreak ParseBreak() { if (this.CurrentLoopOrSwitch == null) { throw ParseException("break outside loop or switch"); } CiBreak result = new CiBreak(this.CurrentLoopOrSwitch) { Line = this.Line }; Expect(CiToken.Break); Expect(CiToken.Semicolon); if (this.CurrentLoopOrSwitch is CiLoop loop) { loop.HasBreak = true; } return(result); } CiContinue ParseContinue() { if (this.CurrentLoop == null) { throw ParseException("continue outside loop"); } CiContinue result = new CiContinue(this.CurrentLoop) { Line = this.Line }; Expect(CiToken.Continue); Expect(CiToken.Semicolon); return(result); } void ParseLoopBody(CiLoop loop) { CiLoop outerLoop = this.CurrentLoop; CiCondCompletionStatement outerLoopOrSwitch = this.CurrentLoopOrSwitch; this.CurrentLoopOrSwitch = this.CurrentLoop = loop; loop.Body = ParseStatement(); this.CurrentLoopOrSwitch = outerLoopOrSwitch; this.CurrentLoop = outerLoop; } CiDoWhile ParseDoWhile() { CiDoWhile result = new CiDoWhile { Line = this.Line }; Expect(CiToken.Do); ParseLoopBody(result); Expect(CiToken.While); result.Cond = ParseParenthesized(); Expect(CiToken.Semicolon); return(result); } CiFor ParseFor() { CiFor result = new CiFor { Line = this.Line }; Expect(CiToken.For); Expect(CiToken.LeftParenthesis); if (!See(CiToken.Semicolon)) { result.Init = ParseAssign(true); } Expect(CiToken.Semicolon); if (!See(CiToken.Semicolon)) { result.Cond = ParseExpr(); } Expect(CiToken.Semicolon); if (!See(CiToken.RightParenthesis)) { result.Advance = ParseAssign(false); } Expect(CiToken.RightParenthesis); ParseLoopBody(result); return(result); } void ParseForeachIterator(CiForeach result) { result.Add(new CiVar { Line = this.Line, TypeExpr = ParseType(), Name = ParseId() }); } CiForeach ParseForeach() { CiForeach result = new CiForeach { Line = this.Line }; Expect(CiToken.Foreach); Expect(CiToken.LeftParenthesis); if (Eat(CiToken.LeftParenthesis)) { ParseForeachIterator(result); Expect(CiToken.Comma); ParseForeachIterator(result); Expect(CiToken.RightParenthesis); } else { ParseForeachIterator(result); } Expect(CiToken.In); result.Collection = ParseExpr(); Expect(CiToken.RightParenthesis); ParseLoopBody(result); return(result); } CiIf ParseIf() { CiIf result = new CiIf { Line = this.Line }; Expect(CiToken.If); result.Cond = ParseParenthesized(); result.OnTrue = ParseStatement(); if (Eat(CiToken.Else)) { result.OnFalse = ParseStatement(); } return(result); } CiNative ParseNative() { int line = this.Line; Expect(CiToken.Native); StringBuilder sb = new StringBuilder(); this.CopyTo = sb; try { Expect(CiToken.LeftBrace); int nesting = 1; for (;;) { if (See(CiToken.EndOfFile)) { throw ParseException("Native block not terminated"); } if (See(CiToken.LeftBrace)) { nesting++; } else if (See(CiToken.RightBrace) && --nesting == 0) { break; } NextToken(); } } finally { this.CopyTo = null; } NextToken(); Trace.Assert(sb[sb.Length - 1] == '}'); sb.Length--; return(new CiNative { Line = line, Content = sb.ToString() }); } CiReturn ParseReturn() { CiReturn result = new CiReturn { Line = this.Line }; NextToken(); if (!See(CiToken.Semicolon)) { result.Value = ParseExpr(); } Expect(CiToken.Semicolon); return(result); } CiSwitch ParseSwitch() { CiSwitch result = new CiSwitch { Line = this.Line }; Expect(CiToken.Switch); result.Value = ParseParenthesized(); Expect(CiToken.LeftBrace); CiCondCompletionStatement outerLoopOrSwitch = this.CurrentLoopOrSwitch; this.CurrentLoopOrSwitch = result; List <CiCase> cases = new List <CiCase>(); while (Eat(CiToken.Case)) { List <CiExpr> values = new List <CiExpr>(); CiExpr value; do { value = ParseExpr(); values.Add(value); Expect(CiToken.Colon); } while (Eat(CiToken.Case)); if (See(CiToken.Default)) { throw StatementException(value, "Please remove case before default"); } CiCase kase = new CiCase { Values = values.ToArray() }; List <CiStatement> statements = new List <CiStatement>(); for (;;) { statements.Add(ParseStatement()); switch (this.CurrentToken) { case CiToken.Case: case CiToken.Default: case CiToken.RightBrace: break; default: continue; } break; } kase.Body = statements.ToArray(); cases.Add(kase); } if (cases.Count == 0) { throw ParseException("Switch with no cases"); } result.Cases = cases.ToArray(); if (Eat(CiToken.Default)) { Expect(CiToken.Colon); List <CiStatement> statements = new List <CiStatement>(); do { statements.Add(ParseStatement()); }while (!See(CiToken.RightBrace)); result.DefaultBody = statements.ToArray(); } Expect(CiToken.RightBrace); this.CurrentLoopOrSwitch = outerLoopOrSwitch; return(result); } CiThrow ParseThrow() { CiThrow result = new CiThrow { Line = this.Line }; Expect(CiToken.Throw); result.Message = ParseExpr(); Expect(CiToken.Semicolon); return(result); } CiWhile ParseWhile() { CiWhile result = new CiWhile { Line = this.Line }; Expect(CiToken.While); result.Cond = ParseParenthesized(); ParseLoopBody(result); return(result); } CiStatement ParseStatement() { switch (this.CurrentToken) { case CiToken.LeftBrace: return(ParseBlock()); case CiToken.Assert: return(ParseAssert()); case CiToken.Break: return(ParseBreak()); case CiToken.Const: return(ParseConst()); case CiToken.Continue: return(ParseContinue()); case CiToken.Do: return(ParseDoWhile()); case CiToken.For: return(ParseFor()); case CiToken.Foreach: return(ParseForeach()); case CiToken.If: return(ParseIf()); case CiToken.Native: return(ParseNative()); case CiToken.Return: return(ParseReturn()); case CiToken.Switch: return(ParseSwitch()); case CiToken.Throw: return(ParseThrow()); case CiToken.While: return(ParseWhile()); default: CiExpr expr = ParseAssign(true); Expect(CiToken.Semicolon); return(expr); } } CiCallType ParseCallType() { switch (this.CurrentToken) { case CiToken.Static: NextToken(); return(CiCallType.Static); case CiToken.Abstract: NextToken(); return(CiCallType.Abstract); case CiToken.Virtual: NextToken(); return(CiCallType.Virtual); case CiToken.Override: NextToken(); return(CiCallType.Override); case CiToken.Sealed: NextToken(); return(CiCallType.Sealed); default: return(CiCallType.Normal); } } void ParseMethod(CiMethod method) { method.IsMutator = Eat(CiToken.ExclamationMark); Expect(CiToken.LeftParenthesis); if (!See(CiToken.RightParenthesis)) { do { CiCodeDoc doc = ParseDoc(); CiVar param = ParseVar(); param.Documentation = doc; method.Parameters.Add(param); } while (Eat(CiToken.Comma)); } Expect(CiToken.RightParenthesis); method.Throws = Eat(CiToken.Throws); if (method.CallType == CiCallType.Abstract) { Expect(CiToken.Semicolon); } else if (See(CiToken.FatArrow)) { method.Body = ParseReturn(); } else { method.Body = ParseBlock(); } } public CiClass ParseClass(CiCallType callType) { Expect(CiToken.Class); CiClass klass = new CiClass { Parent = this.Program, Filename = this.Filename, Line = this.Line, CallType = callType, Name = ParseId() }; if (Eat(CiToken.Colon)) { klass.BaseClassName = ParseId(); } Expect(CiToken.LeftBrace); List <CiConst> consts = new List <CiConst>(); List <CiField> fields = new List <CiField>(); List <CiMethod> methods = new List <CiMethod>(); while (!Eat(CiToken.RightBrace)) { CiCodeDoc doc = ParseDoc(); CiVisibility visibility; switch (this.CurrentToken) { case CiToken.Internal: visibility = CiVisibility.Internal; NextToken(); break; case CiToken.Protected: visibility = CiVisibility.Protected; NextToken(); break; case CiToken.Public: visibility = CiVisibility.Public; NextToken(); break; default: visibility = CiVisibility.Private; break; } if (See(CiToken.Const)) { // const CiConst konst = ParseConst(); konst.Documentation = doc; konst.Visibility = visibility; consts.Add(konst); continue; } callType = ParseCallType(); // \ class | static | normal | abstract | sealed // method \| | | | // --------+--------+--------+----------+------- // static | + | + | + | + // normal | - | + | + | + // abstract| - | - | + | - // virtual | - | + | + | - // override| - | + | + | + // sealed | - | + | + | + if (callType == CiCallType.Static || klass.CallType == CiCallType.Abstract) { // ok } else if (klass.CallType == CiCallType.Static) { throw ParseException("Only static members allowed in a static class"); } else if (callType == CiCallType.Abstract) { throw ParseException("Abstract methods allowed only in an abstract class"); } else if (klass.CallType == CiCallType.Sealed && callType == CiCallType.Virtual) { throw ParseException("Virtual methods disallowed in a sealed class"); } if (visibility == CiVisibility.Private && callType != CiCallType.Static && callType != CiCallType.Normal) { throw ParseException("{0} method cannot be private", callType); } CiExpr type = Eat(CiToken.Void) ? null : ParseType(); if (See(CiToken.LeftBrace) && type is CiCallExpr call) { // constructor if (call.Method.Name != klass.Name) { throw ParseException("Constructor name doesn't match class name"); } if (callType != CiCallType.Normal) { throw ParseException("Constructor cannot be {0}", callType); } if (call.Arguments.Length != 0) { throw ParseException("Constructor parameters not supported"); } if (klass.Constructor != null) { throw ParseException("Duplicate constructor, already defined in line {0}", klass.Constructor.Line); } if (visibility == CiVisibility.Private) { visibility = CiVisibility.Internal; // TODO } klass.Constructor = new CiMethodBase { Line = call.Line, Documentation = doc, Visibility = visibility, Parent = klass, Name = klass.Name, Body = ParseBlock() }; continue; } int line = this.Line; string name = ParseId(); if (See(CiToken.LeftParenthesis) || See(CiToken.ExclamationMark)) { // method CiMethod method = new CiMethod { Line = line, Documentation = doc, Visibility = visibility, CallType = callType, TypeExpr = type, Name = name }; method.Parameters.Parent = klass; ParseMethod(method); methods.Add(method); continue; } // field if (visibility == CiVisibility.Public) { throw ParseException("Field cannot be public"); } if (callType != CiCallType.Normal) { throw ParseException("Field cannot be {0}", callType); } if (type == null) { throw ParseException("Field cannot be void"); } CiField field = new CiField { Line = line, Documentation = doc, Visibility = visibility, TypeExpr = type, Name = name }; if (Eat(CiToken.Assign)) { field.Value = ParseExpr(); } Expect(CiToken.Semicolon); fields.Add(field); } klass.Consts = consts.ToArray(); klass.Fields = fields.ToArray(); klass.Methods = methods.ToArray(); return(klass); } public CiEnum ParseEnum() { Expect(CiToken.Enum); CiEnum enu = new CiEnum { Parent = this.Program, Filename = this.Filename, IsFlags = Eat(CiToken.Asterisk), Line = this.Line, Name = ParseId() }; Expect(CiToken.LeftBrace); do { CiConst konst = new CiConst { Visibility = CiVisibility.Public, Documentation = ParseDoc(), Line = this.Line, Name = ParseId(), Type = enu }; if (Eat(CiToken.Assign)) { konst.Value = ParseExpr(); } else if (enu.IsFlags) { throw ParseException("enum* symbol must be assigned a value"); } enu.Add(konst); } while (Eat(CiToken.Comma)); Expect(CiToken.RightBrace); return(enu); } public void Parse(string filename, TextReader reader) { Open(filename, reader); while (!See(CiToken.EndOfFile)) { CiCodeDoc doc = ParseDoc(); CiContainerType type; bool isPublic = Eat(CiToken.Public); switch (this.CurrentToken) { // class case CiToken.Class: type = ParseClass(CiCallType.Normal); break; case CiToken.Static: case CiToken.Abstract: case CiToken.Sealed: type = ParseClass(ParseCallType()); break; // enum case CiToken.Enum: type = ParseEnum(); break; // native case CiToken.Native: this.Program.TopLevelNatives.Add(ParseNative().Content); continue; default: throw ParseException("Expected class or enum"); } type.Documentation = doc; type.IsPublic = isPublic; this.Program.Add(type); } } } }
CiType LookupType(string name) { CiSymbol symbol = this.Symbols.TryLookup(name); if (symbol is CiType) { return((CiType)symbol); } if (symbol is CiClass) { return new CiClassPtrType { Name = name, Class = (CiClass)symbol } } ; if (symbol == null) { CiType unknown = new CiUnknownType(); unknown.Name = name; return(unknown); } throw new ParseException("{0} is not a type", name); } CiType ParseArrayType(CiType baseType) { if (Eat(CiToken.LeftBracket)) { if (Eat(CiToken.RightBracket)) { return new CiArrayPtrType { ElementType = ParseArrayType(baseType) } } ; CiExpr len = ParseExpr(); Expect(CiToken.RightBracket); return(new CiArrayStorageType { LengthExpr = len, ElementType = ParseArrayType(baseType) }); } return(baseType); } CiType ParseType() { string baseName = ParseId(); CiType baseType; if (Eat(CiToken.LeftParenthesis)) { if (baseName == "string") { baseType = new CiStringStorageType { LengthExpr = ParseExpr() }; Expect(CiToken.RightParenthesis); } else { Expect(CiToken.RightParenthesis); baseType = new CiClassStorageType { Name = baseName, Class = new CiUnknownClass { Name = baseName } }; } } else { baseType = LookupType(baseName); } return(ParseArrayType(baseType)); } object ParseConstInitializer(CiType type) { if (type is CiArrayType) { Expect(CiToken.LeftBrace); CiType elementType = ((CiArrayType)type).ElementType; List <object> list = new List <object>(); if (!See(CiToken.RightBrace)) { do { list.Add(ParseConstInitializer(elementType)); }while (Eat(CiToken.Comma)); } Expect(CiToken.RightBrace); return(list.ToArray()); } return(ParseExpr()); } CiConst ParseConst() { Expect(CiToken.Const); CiConst konst = new CiConst(); konst.Type = ParseType(); konst.Name = ParseId(); Expect(CiToken.Assign); konst.Value = ParseConstInitializer(konst.Type); Expect(CiToken.Semicolon); if (this.Symbols.Parent != null && konst.Type is CiArrayType) { this.ConstArrays.Add(konst); konst.GlobalName = "CiConstArray_" + this.ConstArrays.Count; } return(konst); } CiBinaryResourceExpr ParseBinaryResource() { Expect(CiToken.LeftParenthesis); CiExpr nameExpr = ParseExpr(); Expect(CiToken.RightParenthesis); return(new CiBinaryResourceExpr { NameExpr = nameExpr }); } CiExpr ParsePrimaryExpr() { if (See(CiToken.Increment) || See(CiToken.Decrement) || See(CiToken.Minus) || See(CiToken.Not)) { CiToken op = this.CurrentToken; NextToken(); CiExpr inner = ParsePrimaryExpr(); return(new CiUnaryExpr { Op = op, Inner = inner }); } if (Eat(CiToken.CondNot)) { CiExpr inner = ParsePrimaryExpr(); return(new CiCondNotExpr { Inner = inner }); } CiExpr result; if (See(CiToken.IntConstant)) { result = new CiConstExpr(this.CurrentInt); NextToken(); } else if (See(CiToken.StringConstant)) { result = new CiConstExpr(this.CurrentString); NextToken(); } else if (Eat(CiToken.LeftParenthesis)) { result = ParseExpr(); Expect(CiToken.RightParenthesis); } else if (See(CiToken.Id)) { string name = ParseId(); if (name == "BinaryResource") { result = ParseBinaryResource(); } else { CiSymbol symbol = this.Symbols.TryLookup(name); if (symbol is CiMacro) { Expand((CiMacro)symbol); Expect(CiToken.LeftParenthesis); result = ParseExpr(); Expect(CiToken.RightParenthesis); } else { if (symbol == null) { symbol = new CiUnknownSymbol { Name = name } } ; result = new CiSymbolAccess { Symbol = symbol }; } } } else if (Eat(CiToken.New)) { CiType newType = ParseType(); if (!(newType is CiClassStorageType || newType is CiArrayStorageType)) { throw new ParseException("'new' syntax error"); } result = new CiNewExpr { NewType = newType }; } else { throw new ParseException("Invalid expression"); } for (;;) { if (Eat(CiToken.Dot)) { result = new CiUnknownMemberAccess { Parent = result, Name = ParseId() } } ; else if (Eat(CiToken.LeftParenthesis)) { CiMethodCall call = new CiMethodCall(); call.Obj = result; List <CiExpr> args = new List <CiExpr>(); if (!See(CiToken.RightParenthesis)) { do { args.Add(ParseExpr()); }while (Eat(CiToken.Comma)); } Expect(CiToken.RightParenthesis); call.Arguments = args.ToArray(); result = call; } else if (Eat(CiToken.LeftBracket)) { CiExpr index = ParseExpr(); Expect(CiToken.RightBracket); result = new CiIndexAccess { Parent = result, Index = index }; } else if (See(CiToken.Increment) || See(CiToken.Decrement)) { CiToken op = this.CurrentToken; NextToken(); return(new CiPostfixExpr { Inner = result, Op = op }); } else { return(result); } } } CiExpr ParseMulExpr() { CiExpr left = ParsePrimaryExpr(); while (See(CiToken.Asterisk) || See(CiToken.Slash) || See(CiToken.Mod)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBinaryExpr { Left = left, Op = op, Right = ParsePrimaryExpr() }; } return(left); } CiExpr ParseAddExpr() { CiExpr left = ParseMulExpr(); while (See(CiToken.Plus) || See(CiToken.Minus)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBinaryExpr { Left = left, Op = op, Right = ParseMulExpr() }; } return(left); } CiExpr ParseShiftExpr() { CiExpr left = ParseAddExpr(); while (See(CiToken.ShiftLeft) || See(CiToken.ShiftRight)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBinaryExpr { Left = left, Op = op, Right = ParseAddExpr() }; } return(left); } CiExpr ParseRelExpr() { CiExpr left = ParseShiftExpr(); while (See(CiToken.Less) || See(CiToken.LessOrEqual) || See(CiToken.Greater) || See(CiToken.GreaterOrEqual)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBoolBinaryExpr { Left = left, Op = op, Right = ParseShiftExpr() }; } return(left); } CiExpr ParseEqualityExpr() { CiExpr left = ParseRelExpr(); while (See(CiToken.Equal) || See(CiToken.NotEqual)) { CiToken op = this.CurrentToken; NextToken(); left = new CiBoolBinaryExpr { Left = left, Op = op, Right = ParseRelExpr() }; } return(left); } CiExpr ParseAndExpr() { CiExpr left = ParseEqualityExpr(); while (Eat(CiToken.And)) { left = new CiBinaryExpr { Left = left, Op = CiToken.And, Right = ParseEqualityExpr() } } ; return(left); } CiExpr ParseXorExpr() { CiExpr left = ParseAndExpr(); while (Eat(CiToken.Xor)) { left = new CiBinaryExpr { Left = left, Op = CiToken.Xor, Right = ParseAndExpr() } } ; return(left); } CiExpr ParseOrExpr() { CiExpr left = ParseXorExpr(); while (Eat(CiToken.Or)) { left = new CiBinaryExpr { Left = left, Op = CiToken.Or, Right = ParseXorExpr() } } ; return(left); } CiExpr ParseCondAndExpr() { CiExpr left = ParseOrExpr(); while (Eat(CiToken.CondAnd)) { left = new CiBoolBinaryExpr { Left = left, Op = CiToken.CondAnd, Right = ParseOrExpr() } } ; return(left); } CiExpr ParseCondOrExpr() { CiExpr left = ParseCondAndExpr(); while (Eat(CiToken.CondOr)) { left = new CiBoolBinaryExpr { Left = left, Op = CiToken.CondOr, Right = ParseCondAndExpr() } } ; return(left); } CiExpr ParseExpr() { CiExpr left = ParseCondOrExpr(); if (Eat(CiToken.QuestionMark)) { CiCondExpr result = new CiCondExpr(); result.Cond = left; result.OnTrue = ParseExpr(); Expect(CiToken.Colon); result.OnFalse = ParseExpr(); return(result); } return(left); } CiMaybeAssign ParseMaybeAssign() { CiExpr left = ParseExpr(); CiToken op = this.CurrentToken; if (op == CiToken.Assign || op == CiToken.AddAssign || op == CiToken.SubAssign || op == CiToken.MulAssign || op == CiToken.DivAssign || op == CiToken.ModAssign || op == CiToken.AndAssign || op == CiToken.OrAssign || op == CiToken.XorAssign || op == CiToken.ShiftLeftAssign || op == CiToken.ShiftRightAssign) { NextToken(); CiAssign result = new CiAssign(); result.Target = left; result.Op = op; result.Source = ParseMaybeAssign(); return(result); } return(left); } ICiStatement ParseExprWithSideEffect() { ICiStatement result = ParseMaybeAssign() as ICiStatement; if (result == null) { throw new ParseException("Useless expression"); } return(result); } CiExpr ParseCond() { Expect(CiToken.LeftParenthesis); CiExpr cond = ParseExpr(); Expect(CiToken.RightParenthesis); return(cond); } void OpenScope() { this.Symbols = new SymbolTable { Parent = this.Symbols }; } void CloseScope() { this.Symbols = this.Symbols.Parent; } CiVar ParseVar() { CiVar def = new CiVar(); def.Type = ParseType(); def.Name = ParseId(); if (Eat(CiToken.Assign)) { def.InitialValue = ParseExpr(); } Expect(CiToken.Semicolon); this.Symbols.Add(def); return(def); } ICiStatement ParseVarOrExpr() { string name = this.CurrentString; CiSymbol symbol = this.Symbols.TryLookup(name); if (symbol is CiMacro) { NextToken(); Expand((CiMacro)symbol); return(ParseStatement()); } // try var StringBuilder sb = new StringBuilder(); this.CopyTo = sb; try { return(ParseVar()); } catch (ParseException) { } finally { this.CopyTo = null; } // try expr this.CurrentString = name; this.CurrentToken = CiToken.Id; BeginExpand("ambigous code", sb.ToString(), null); SetReader(new StringReader(sb.ToString())); ICiStatement result = ParseExprWithSideEffect(); Expect(CiToken.Semicolon); return(result); } CiNativeBlock ParseNativeBlock() { StringBuilder sb = new StringBuilder(); this.CopyTo = sb; NextToken(); bool oneLine = true; try { if (See(CiToken.LeftBrace)) { oneLine = false; sb = new StringBuilder(); this.CopyTo = sb; Expect(CiToken.LeftBrace); } int level = 1; for (;;) { if (oneLine && See(CiToken.Semicolon) && level == 1) { break; } if (See(CiToken.EndOfFile)) { throw new ParseException("Native block not terminated"); } if (See(CiToken.LeftBrace)) { level++; } else if (See(CiToken.RightBrace)) { if (--level == 0) { break; } } NextToken(); } } finally { this.CopyTo = null; } NextToken(); Trace.Assert(sb[sb.Length - 1] == '}'); if (!oneLine) { sb.Length--; } else { sb.Append("\n\t"); } return(new CiNativeBlock { Content = sb.ToString() }); } CiSwitch ParseSwitch() { Expect(CiToken.LeftParenthesis); CiSwitch result = new CiSwitch(); result.Value = ParseExpr(); Expect(CiToken.RightParenthesis); Expect(CiToken.LeftBrace); List <CiCase> cases = new List <CiCase>(); while (Eat(CiToken.Case)) { List <object> values = new List <object>(); do { values.Add(ParseExpr()); Expect(CiToken.Colon); } while (Eat(CiToken.Case)); if (See(CiToken.Default)) { throw new ParseException("Please remove case before default"); } CiCase kase = new CiCase { Values = values.ToArray() }; List <ICiStatement> statements = new List <ICiStatement>(); do { statements.Add(ParseStatement()); }while (!See(CiToken.Case) && !See(CiToken.Default) && !See(CiToken.Goto) && !See(CiToken.RightBrace)); kase.Body = statements.ToArray(); if (Eat(CiToken.Goto)) { if (Eat(CiToken.Case)) { kase.FallthroughTo = ParseExpr(); } else if (Eat(CiToken.Default)) { kase.FallthroughTo = null; } else { throw new ParseException("Expected goto case or goto default"); } Expect(CiToken.Semicolon); kase.Fallthrough = true; } cases.Add(kase); } if (cases.Count == 0) { throw new ParseException("Switch with no cases"); } result.Cases = cases.ToArray(); if (Eat(CiToken.Default)) { Expect(CiToken.Colon); List <ICiStatement> statements = new List <ICiStatement>(); do { statements.Add(ParseStatement()); }while (!See(CiToken.RightBrace)); result.DefaultBody = statements.ToArray(); } Expect(CiToken.RightBrace); return(result); } ICiStatement ParseStatement() { while (Eat(CiToken.Macro)) { this.Symbols.Add(ParseMacro()); } if (See(CiToken.Id)) { return(ParseVarOrExpr()); } if (See(CiToken.LeftBrace)) { OpenScope(); CiBlock result = ParseBlock(); CloseScope(); return(result); } if (Eat(CiToken.Break)) { Expect(CiToken.Semicolon); return(new CiBreak()); } if (See(CiToken.Const)) { CiConst konst = ParseConst(); this.Symbols.Add(konst); return(konst); } if (Eat(CiToken.Continue)) { Expect(CiToken.Semicolon); return(new CiContinue()); } if (Eat(CiToken.Delete)) { CiExpr expr = ParseExpr(); Expect(CiToken.Semicolon); return(new CiDelete { Expr = expr }); } if (Eat(CiToken.Do)) { CiDoWhile result = new CiDoWhile(); result.Body = ParseStatement(); Expect(CiToken.While); result.Cond = ParseCond(); Expect(CiToken.Semicolon); return(result); } if (Eat(CiToken.For)) { Expect(CiToken.LeftParenthesis); OpenScope(); CiFor result = new CiFor(); if (See(CiToken.Id)) { result.Init = ParseVarOrExpr(); } else { Expect(CiToken.Semicolon); } if (!See(CiToken.Semicolon)) { result.Cond = ParseExpr(); } Expect(CiToken.Semicolon); if (!See(CiToken.RightParenthesis)) { result.Advance = ParseExprWithSideEffect(); } Expect(CiToken.RightParenthesis); result.Body = ParseStatement(); CloseScope(); return(result); } if (Eat(CiToken.If)) { CiIf result = new CiIf(); result.Cond = ParseCond(); result.OnTrue = ParseStatement(); if (Eat(CiToken.Else)) { result.OnFalse = ParseStatement(); } return(result); } if (See(CiToken.Native)) { return(ParseNativeBlock()); } if (Eat(CiToken.Return)) { CiReturn result = new CiReturn(); if (this.CurrentMethod.Signature.ReturnType != CiType.Void) { result.Value = ParseExpr(); } Expect(CiToken.Semicolon); return(result); } if (Eat(CiToken.Switch)) { return(ParseSwitch()); } if (Eat(CiToken.Throw)) { CiThrow result = new CiThrow(); result.Message = ParseExpr(); Expect(CiToken.Semicolon); return(result); } if (Eat(CiToken.While)) { CiWhile result = new CiWhile(); result.Cond = ParseCond(); result.Body = ParseStatement(); return(result); } throw new ParseException("Invalid statement"); } CiBlock ParseBlock() { Expect(CiToken.LeftBrace); List <ICiStatement> statements = new List <ICiStatement>(); while (!Eat(CiToken.RightBrace)) { statements.Add(ParseStatement()); } return(new CiBlock { Statements = statements.ToArray() }); } CiParam CreateThis() { CiParam thiz = new CiParam(); thiz.Type = new CiClassPtrType { Name = this.CurrentClass.Name, Class = this.CurrentClass }; thiz.Name = "this"; this.Symbols.Add(thiz); return(thiz); } CiType ParseReturnType() { if (Eat(CiToken.Void)) { return(CiType.Void); } return(ParseType()); } CiParam[] ParseParams() { Expect(CiToken.LeftParenthesis); List <CiParam> paramz = new List <CiParam>(); if (!See(CiToken.RightParenthesis)) { do { CiParam param = new CiParam(); param.Documentation = ParseDoc(); param.Type = ParseType(); param.Name = ParseId(); this.Symbols.Add(param); paramz.Add(param); } while (Eat(CiToken.Comma)); } Expect(CiToken.RightParenthesis); return(paramz.ToArray()); } void ParseMethod(CiMethod method) { this.CurrentMethod = method; OpenScope(); if (method.CallType != CiCallType.Static) { method.This = CreateThis(); } method.Signature.Params = ParseParams(); if (method.CallType == CiCallType.Abstract) { Expect(CiToken.Semicolon); } else { method.Body = ParseBlock(); } CloseScope(); this.CurrentMethod = null; } CiMethod ParseConstructor() { NextToken(); Expect(CiToken.LeftParenthesis); Expect(CiToken.RightParenthesis); OpenScope(); CiMethod method = new CiMethod( CiType.Void, "<constructor>") { Class = this.CurrentClass, CallType = CiCallType.Normal, This = CreateThis() }; this.CurrentMethod = method; method.Body = ParseBlock(); CloseScope(); this.CurrentMethod = null; return(method); } CiClass ParseClass() { CiClass klass = new CiClass(); klass.SourceFilename = this.Filename; if (Eat(CiToken.Abstract)) { klass.IsAbstract = true; } Expect(CiToken.Class); klass.Name = ParseId(); if (Eat(CiToken.Colon)) { klass.BaseClass = new CiUnknownClass { Name = ParseId() } } ; Expect(CiToken.LeftBrace); OpenScope(); this.CurrentClass = klass; klass.Members = this.Symbols; while (!Eat(CiToken.RightBrace)) { CiCodeDoc doc = ParseDoc(); CiVisibility visibility = CiVisibility.Private; if (Eat(CiToken.Public)) { visibility = CiVisibility.Public; } else if (Eat(CiToken.Internal)) { visibility = CiVisibility.Internal; } CiSymbol symbol; if (See(CiToken.Const)) { symbol = ParseConst(); ((CiConst)symbol).Class = klass; } else if (Eat(CiToken.Macro)) { if (visibility != CiVisibility.Private) { throw new ParseException("Macros must be private"); } symbol = ParseMacro(); } else { if (See(CiToken.Id) && this.CurrentString == klass.Name) { if (klass.Constructor != null) { throw new ParseException("Duplicate constructor"); } klass.Constructor = ParseConstructor(); continue; } CiCallType callType; if (Eat(CiToken.Static)) { callType = CiCallType.Static; } else if (Eat(CiToken.Abstract)) { if (!klass.IsAbstract) { throw new ParseException("Abstract methods only allowed in abstract classes"); } callType = CiCallType.Abstract; if (visibility == CiVisibility.Private) { visibility = CiVisibility.Internal; } } else if (Eat(CiToken.Virtual)) { callType = CiCallType.Virtual; if (visibility == CiVisibility.Private) { visibility = CiVisibility.Internal; } } else if (Eat(CiToken.Override)) { callType = CiCallType.Override; if (visibility == CiVisibility.Private) { visibility = CiVisibility.Internal; } } else { callType = CiCallType.Normal; } CiType type = ParseReturnType(); string name = ParseId(); if (See(CiToken.LeftParenthesis)) { CiMethod method = new CiMethod(type, name) { Class = klass, CallType = callType }; ParseMethod(method); symbol = method; } else { if (visibility != CiVisibility.Private) { throw new ParseException("Fields must be private"); } if (callType != CiCallType.Normal) { throw new ParseException("Fields cannot be static, abstract, virtual or override"); } if (type == CiType.Void) { throw new ParseException("Field is void"); } Expect(CiToken.Semicolon); symbol = new CiField { Class = klass, Type = type, Name = name }; } } symbol.Documentation = doc; symbol.Visibility = visibility; klass.Members.Add(symbol); } this.CurrentClass = null; CloseScope(); klass.ConstArrays = this.ConstArrays.ToArray(); this.ConstArrays.Clear(); return(klass); } CiDelegate ParseDelegate() { CiDelegate del = new CiDelegate(); Expect(CiToken.Delegate); del.ReturnType = ParseReturnType(); del.Name = ParseId(); OpenScope(); del.Params = ParseParams(); CloseScope(); Expect(CiToken.Semicolon); return(del); } public void Parse(string filename, TextReader reader) { Open(filename, reader); while (!See(CiToken.EndOfFile)) { CiCodeDoc doc = ParseDoc(); bool pub = Eat(CiToken.Public); CiSymbol symbol; if (See(CiToken.Enum)) { symbol = ParseEnum(); } else if (See(CiToken.Class) || See(CiToken.Abstract)) { symbol = ParseClass(); } else if (See(CiToken.Delegate)) { symbol = ParseDelegate(); } else { throw new ParseException("Expected class, enum or delegate"); } symbol.Documentation = doc; symbol.Visibility = pub ? CiVisibility.Public : CiVisibility.Internal; this.Symbols.Add(symbol); } } public CiProgram Program { get { return(new CiProgram { Globals = this.Symbols }); } } } }
CiExpr ParseAddExpr() { CiExpr left = ParseMulExpr(); while (See(CiToken.Plus) || See(CiToken.Minus)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseMulExpr() } } ; return(left); } CiExpr ParseShiftExpr() { CiExpr left = ParseAddExpr(); while (See(CiToken.ShiftLeft) || See(CiToken.ShiftRight)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseAddExpr() } } ; return(left); } CiExpr ParseRelExpr() { CiExpr left = ParseShiftExpr(); for (;;) { switch (this.CurrentToken) { case CiToken.Less: case CiToken.LessOrEqual: case CiToken.Greater: case CiToken.GreaterOrEqual: left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseShiftExpr() }; break; default: return(left); } } } CiExpr ParseEqualityExpr() { CiExpr left = ParseRelExpr(); while (See(CiToken.Equal) || See(CiToken.NotEqual)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseRelExpr() } } ; return(left); } CiExpr ParseAndExpr() { CiExpr left = ParseEqualityExpr(); while (See(CiToken.And)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseEqualityExpr() } } ; return(left); } CiExpr ParseXorExpr() { CiExpr left = ParseAndExpr(); while (See(CiToken.Xor)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseAndExpr() } } ; return(left); } CiExpr ParseOrExpr() { CiExpr left = ParseXorExpr(); while (See(CiToken.Or)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseXorExpr() } } ; return(left); } CiExpr ParseCondAndExpr() { CiExpr left = ParseOrExpr(); while (See(CiToken.CondAnd)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseOrExpr() } } ; return(left); } CiExpr ParseCondOrExpr() { CiExpr left = ParseCondAndExpr(); while (See(CiToken.CondOr)) { left = new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseCondAndExpr() } } ; return(left); } CiExpr ParseExpr() { CiExpr left = ParseCondOrExpr(); if (See(CiToken.QuestionMark)) { CiCondExpr result = new CiCondExpr { Line = this.Line, Cond = left }; NextToken(); result.OnTrue = ParseExpr(); Expect(CiToken.Colon); result.OnFalse = ParseExpr(); return(result); } return(left); } CiExpr ParseType() { CiExpr left = ParseExpr(); if (Eat(CiToken.Range)) { return new CiBinaryExpr { Line = this.Line, Left = left, Op = CiToken.Range, Right = ParseExpr() } } ; return(left); } CiExpr ParseAssign(bool allowVar) { CiExpr left = ParseType(); switch (this.CurrentToken) { case CiToken.Assign: case CiToken.AddAssign: case CiToken.SubAssign: case CiToken.MulAssign: case CiToken.DivAssign: case CiToken.ModAssign: case CiToken.AndAssign: case CiToken.OrAssign: case CiToken.XorAssign: case CiToken.ShiftLeftAssign: case CiToken.ShiftRightAssign: return(new CiBinaryExpr { Line = this.Line, Left = left, Op = NextToken(), Right = ParseAssign(false) }); case CiToken.Id: if (allowVar) { return(ParseVar(left)); } return(left); default: return(left); } } CiVar ParseVar(CiExpr type) { CiVar def = new CiVar { Line = this.Line, TypeExpr = type, Name = ParseId() }; if (Eat(CiToken.Assign)) { def.Value = ParseAssign(false); } return(def); } CiVar ParseVar() { return(ParseVar(ParseType())); } CiConst ParseConst() { Expect(CiToken.Const); CiConst konst = new CiConst { Line = this.Line, TypeExpr = ParseType(), Name = ParseId() }; Expect(CiToken.Assign); konst.Value = ParseConstInitializer(); Expect(CiToken.Semicolon); return(konst); } CiBlock ParseBlock() { int line = this.Line; Expect(CiToken.LeftBrace); List <CiStatement> statements = new List <CiStatement>(); while (!Eat(CiToken.RightBrace)) { statements.Add(ParseStatement()); } return(new CiBlock { Filename = this.Filename, Line = this.Line, Statements = statements.ToArray() }); } CiBreak ParseBreak() { if (this.CurrentLoopOrSwitch == null) { throw ParseException("break outside loop or switch"); } CiBreak result = new CiBreak(this.CurrentLoopOrSwitch) { Line = this.Line }; Expect(CiToken.Break); Expect(CiToken.Semicolon); return(result); } CiContinue ParseContinue() { if (this.CurrentLoop == null) { throw ParseException("continue outside loop"); } CiContinue result = new CiContinue(this.CurrentLoop) { Line = this.Line }; Expect(CiToken.Continue); Expect(CiToken.Semicolon); return(result); } void ParseLoopBody(CiLoop loop) { CiLoop outerLoop = this.CurrentLoop; CiCondCompletionStatement outerLoopOrSwitch = this.CurrentLoopOrSwitch; this.CurrentLoopOrSwitch = this.CurrentLoop = loop; loop.Body = ParseStatement(); this.CurrentLoopOrSwitch = outerLoopOrSwitch; this.CurrentLoop = outerLoop; } CiDoWhile ParseDoWhile() { CiDoWhile result = new CiDoWhile { Line = this.Line }; Expect(CiToken.Do); ParseLoopBody(result); Expect(CiToken.While); result.Cond = ParseParenthesized(); Expect(CiToken.Semicolon); return(result); } CiFor ParseFor() { CiFor result = new CiFor { Line = this.Line }; Expect(CiToken.For); Expect(CiToken.LeftParenthesis); if (!See(CiToken.Semicolon)) { result.Init = ParseAssign(true); } Expect(CiToken.Semicolon); if (!See(CiToken.Semicolon)) { result.Cond = ParseExpr(); } Expect(CiToken.Semicolon); if (!See(CiToken.RightParenthesis)) { result.Advance = ParseAssign(false); } Expect(CiToken.RightParenthesis); ParseLoopBody(result); return(result); } CiForeach ParseForeach() { CiForeach result = new CiForeach { Line = this.Line }; Expect(CiToken.Foreach); Expect(CiToken.LeftParenthesis); result.Element = new CiVar { Line = this.Line, TypeExpr = ParseType(), Name = ParseId() }; Expect(CiToken.In); result.Collection = ParseExpr(); Expect(CiToken.RightParenthesis); ParseLoopBody(result); return(result); } CiIf ParseIf() { CiIf result = new CiIf { Line = this.Line }; Expect(CiToken.If); result.Cond = ParseParenthesized(); result.OnTrue = ParseStatement(); if (Eat(CiToken.Else)) { result.OnFalse = ParseStatement(); } return(result); } CiNative ParseNative() { int line = this.Line; Expect(CiToken.Native); StringBuilder sb = new StringBuilder(); this.CopyTo = sb; try { Expect(CiToken.LeftBrace); int nesting = 1; for (;;) { if (See(CiToken.EndOfFile)) { throw ParseException("Native block not terminated"); } if (See(CiToken.LeftBrace)) { nesting++; } else if (See(CiToken.RightBrace) && --nesting == 0) { break; } NextToken(); } } finally { this.CopyTo = null; } NextToken(); Trace.Assert(sb[sb.Length - 1] == '}'); sb.Length--; return(new CiNative { Line = line, Content = sb.ToString() }); } CiReturn ParseReturn() { CiReturn result = new CiReturn { Line = this.Line }; NextToken(); if (!See(CiToken.Semicolon)) { result.Value = ParseExpr(); } Expect(CiToken.Semicolon); return(result); } CiSwitch ParseSwitch() { CiSwitch result = new CiSwitch { Line = this.Line }; Expect(CiToken.Switch); result.Value = ParseParenthesized(); Expect(CiToken.LeftBrace); CiCondCompletionStatement outerLoopOrSwitch = this.CurrentLoopOrSwitch; this.CurrentLoopOrSwitch = result; List <CiCase> cases = new List <CiCase>(); while (Eat(CiToken.Case)) { List <CiExpr> values = new List <CiExpr>(); CiExpr value; do { value = ParseExpr(); values.Add(value); Expect(CiToken.Colon); } while (Eat(CiToken.Case)); if (See(CiToken.Default)) { throw StatementException(value, "Please remove case before default"); } CiCase kase = new CiCase { Values = values.ToArray() }; List <CiStatement> statements = new List <CiStatement>(); for (;;) { statements.Add(ParseStatement()); switch (this.CurrentToken) { case CiToken.Case: case CiToken.Default: case CiToken.RightBrace: break; case CiToken.Goto: NextToken(); switch (this.CurrentToken) { case CiToken.Case: NextToken(); kase.Fallthrough = ParseExpr(); break; case CiToken.Default: kase.Fallthrough = new CiGotoDefault { Line = this.Line }; NextToken(); break; default: throw ParseException("Expected goto case or goto default"); } Expect(CiToken.Semicolon); break; default: continue; } break; } kase.Body = statements.ToArray(); cases.Add(kase); } if (cases.Count == 0) { throw ParseException("Switch with no cases"); } result.Cases = cases.ToArray(); if (Eat(CiToken.Default)) { Expect(CiToken.Colon); List <CiStatement> statements = new List <CiStatement>(); do { statements.Add(ParseStatement()); }while (!See(CiToken.RightBrace)); result.DefaultBody = statements.ToArray(); } Expect(CiToken.RightBrace); this.CurrentLoopOrSwitch = outerLoopOrSwitch; return(result); } CiThrow ParseThrow() { CiThrow result = new CiThrow { Line = this.Line }; Expect(CiToken.Throw); result.Message = ParseExpr(); Expect(CiToken.Semicolon); return(result); } CiWhile ParseWhile() { CiWhile result = new CiWhile { Line = this.Line }; Expect(CiToken.While); result.Cond = ParseParenthesized(); ParseLoopBody(result); return(result); } CiStatement ParseStatement() { switch (this.CurrentToken) { case CiToken.LeftBrace: return(ParseBlock()); case CiToken.Break: return(ParseBreak()); case CiToken.Const: return(ParseConst()); case CiToken.Continue: return(ParseContinue()); case CiToken.Do: return(ParseDoWhile()); case CiToken.For: return(ParseFor()); case CiToken.Foreach: return(ParseForeach()); case CiToken.If: return(ParseIf()); case CiToken.Native: return(ParseNative()); case CiToken.Return: return(ParseReturn()); case CiToken.Switch: return(ParseSwitch()); case CiToken.Throw: return(ParseThrow()); case CiToken.While: return(ParseWhile()); default: CiExpr expr = ParseAssign(true); Expect(CiToken.Semicolon); return(expr); } } CiCallType ParseCallType() { switch (this.CurrentToken) { case CiToken.Static: NextToken(); return(CiCallType.Static); case CiToken.Abstract: NextToken(); return(CiCallType.Abstract); case CiToken.Virtual: NextToken(); return(CiCallType.Virtual); case CiToken.Override: NextToken(); return(CiCallType.Override); case CiToken.Sealed: NextToken(); return(CiCallType.Sealed); default: return(CiCallType.Normal); } } void ParseMethod(CiMethod method) { method.IsMutator = Eat(CiToken.ExclamationMark); method.Parameters.Filename = this.Filename; Expect(CiToken.LeftParenthesis); if (!See(CiToken.RightParenthesis)) { do { CiCodeDoc doc = ParseDoc(); CiVar param = ParseVar(); param.Documentation = doc; method.Parameters.Add(param); } while (Eat(CiToken.Comma)); } Expect(CiToken.RightParenthesis); method.Throws = Eat(CiToken.Throws); if (method.CallType == CiCallType.Abstract) { Expect(CiToken.Semicolon); } else if (See(CiToken.FatArrow)) { method.Body = ParseReturn(); } else { method.Body = ParseBlock(); } } public CiClass ParseClass(CiCallType callType) { Expect(CiToken.Class); CiClass klass = new CiClass { Parent = this.Program, Filename = this.Filename, Line = this.Line, CallType = callType, Name = ParseId() }; if (Eat(CiToken.Colon)) { klass.BaseClassName = ParseId(); } Expect(CiToken.LeftBrace); List <CiConst> consts = new List <CiConst>(); List <CiField> fields = new List <CiField>(); List <CiMethod> methods = new List <CiMethod>(); while (!Eat(CiToken.RightBrace)) { CiCodeDoc doc = ParseDoc(); CiVisibility visibility; switch (this.CurrentToken) { case CiToken.Internal: visibility = CiVisibility.Internal; NextToken(); break; case CiToken.Protected: visibility = CiVisibility.Protected; NextToken(); break; case CiToken.Public: visibility = CiVisibility.Public; NextToken(); break; default: visibility = CiVisibility.Private; break; } if (See(CiToken.Const)) { // const CiConst konst = ParseConst(); konst.Documentation = doc; konst.Visibility = visibility; consts.Add(konst); continue; } callType = ParseCallType(); // \ class | static | normal | abstract | sealed // method \| | | | // --------+--------+--------+----------+------- // static | + | + | + | + // normal | - | + | + | + // abstract| - | - | + | - // virtual | - | + | + | - // override| - | + | + | + // sealed | - | + | + | + if (callType == CiCallType.Static || klass.CallType == CiCallType.Abstract) { // ok } else if (klass.CallType == CiCallType.Static) { throw ParseException("Only static members allowed in a static class"); } else if (callType == CiCallType.Abstract) { throw ParseException("Abstract methods allowed only in an abstract class"); } else if (klass.CallType == CiCallType.Sealed && callType == CiCallType.Virtual) { throw ParseException("Virtual methods disallowed in a sealed class"); } if (visibility == CiVisibility.Private && callType != CiCallType.Static && callType != CiCallType.Normal) { throw ParseException("{0} method cannot be private", callType); } CiExpr type = Eat(CiToken.Void) ? null : ParseType(); if (See(CiToken.LeftBrace) && type is CiBinaryExpr call && call.Op == CiToken.LeftParenthesis && call.Left is CiSymbolReference sr) { // constructor if (sr.Name != klass.Name) { throw ParseException("Constructor name doesn't match class name"); } if (callType != CiCallType.Normal) { throw ParseException("Constructor cannot be {0}", callType); } if (call.RightCollection.Length != 0) { throw ParseException("Constructor parameters not supported"); } if (klass.Constructor != null) { throw ParseException("Duplicate constructor, already defined in line {0}", klass.Constructor.Line); } if (visibility == CiVisibility.Private) { visibility = CiVisibility.Internal; // TODO } klass.Constructor = new CiMethodBase { Line = sr.Line, Documentation = doc, Visibility = visibility, Parent = klass, Name = klass.Name, Body = ParseBlock() }; continue; } int line = this.Line; string name = ParseId(); if (See(CiToken.LeftParenthesis) || See(CiToken.ExclamationMark)) { // method CiMethod method = new CiMethod { Line = line, Documentation = doc, Visibility = visibility, CallType = callType, TypeExpr = type, Name = name }; method.Parameters.Parent = klass; ParseMethod(method); methods.Add(method); continue; } // field if (visibility == CiVisibility.Public) { throw ParseException("Field cannot be public"); } if (callType != CiCallType.Normal) { throw ParseException("Field cannot be {0}", callType); } if (type == null) { throw ParseException("Field cannot be void"); } CiField field = new CiField { Line = line, Documentation = doc, Visibility = visibility, TypeExpr = type, Name = name }; if (Eat(CiToken.Assign)) { field.Value = ParseExpr(); } Expect(CiToken.Semicolon); fields.Add(field); } klass.Consts = consts.ToArray(); klass.Fields = fields.ToArray(); klass.Methods = methods.ToArray(); return(klass); } public CiEnum ParseEnum() { Expect(CiToken.Enum); CiEnum enu = new CiEnum { Parent = this.Program, Filename = this.Filename, IsFlags = Eat(CiToken.Asterisk), Line = this.Line, Name = ParseId() }; Expect(CiToken.LeftBrace); do { CiConst konst = new CiConst { Documentation = ParseDoc(), Line = this.Line, Name = ParseId(), Type = enu }; if (Eat(CiToken.Assign)) { konst.Value = ParseExpr(); } else if (enu.IsFlags) { throw ParseException("enum* symbol must be assigned a value"); } enu.Add(konst); } while (Eat(CiToken.Comma)); Expect(CiToken.RightBrace); return(enu); } public void Parse(string filename, TextReader reader) { Open(filename, reader); while (!See(CiToken.EndOfFile)) { CiCodeDoc doc = ParseDoc(); CiContainerType type; bool isPublic = Eat(CiToken.Public); switch (this.CurrentToken) { // class case CiToken.Class: type = ParseClass(CiCallType.Normal); break; case CiToken.Static: case CiToken.Abstract: case CiToken.Sealed: type = ParseClass(ParseCallType()); break; // enum case CiToken.Enum: type = ParseEnum(); break; // native case CiToken.Native: this.Program.TopLevelNatives.Add(ParseNative().Content); continue; default: throw ParseException("Expected class or enum"); } type.Documentation = doc; type.IsPublic = isPublic; this.Program.Add(type); } } } }
protected override void Write(CiBinaryExpr expr) { switch (expr.Op) { case CiToken.Equal: case CiToken.NotEqual: if (expr.Left.Type is CiStringType && !expr.Left.IsConst(null) && !expr.Right.IsConst(null)) { if (expr.Op == CiToken.NotEqual) Write('!'); Write(expr.Left); Write(".equals("); Write(expr.Right); Write(')'); return; } break; default: break; } base.Write(expr); }
protected override void Write(CiMethodCall expr) { if (expr.Method == CiLibrary.MulDivMethod) { #if USE_INTEGER // FIXME: overflow on 32-bit perl Write("("); #else Write("int("); #endif WriteMulDiv(3, expr); } else if (expr.Method == CiLibrary.CharAtMethod) { Write("ord(substr("); Write(expr.Obj); Write(", "); Write(expr.Arguments[0]); Write(", 1))"); } else if (expr.Method == CiLibrary.SubstringMethod) { Write("substr("); Write(expr.Obj); Write(", "); Write(expr.Arguments[0]); Write(", "); Write(expr.Arguments[1]); Write(')'); } else if (expr.Method == CiLibrary.ArrayCopyToMethod) { CiExpr lenMinus1 = new CiBinaryExpr { Left = expr.Arguments[3], Op = CiToken.Minus, Right = new CiConstExpr(1) }; WriteSlice(expr.Arguments[1], expr.Arguments[2], lenMinus1); Write(" = "); WriteSlice(expr.Obj, expr.Arguments[0], lenMinus1); } else if (expr.Method == CiLibrary.ArrayToStringMethod) { CiExpr lenMinus1 = new CiBinaryExpr { Left = expr.Arguments[1], Op = CiToken.Minus, Right = new CiConstExpr(1) }; Write("pack('U*', "); WriteSlice(expr.Obj, expr.Arguments[0], lenMinus1); Write(')'); } else if (expr.Method == CiLibrary.ArrayStorageClearMethod) { Write('@'); if (expr.Obj is CiVarAccess) { Write(((CiVarAccess)expr.Obj).Var.Name); } else { Write('{'); Write(expr.Obj); Write('}'); } Write(" = (0) x "); Write(((CiArrayStorageType)expr.Obj.Type).Length); } else { if (expr.Method != null) { if (expr.Obj != null) { Write(expr.Obj); Write("->"); } else { Write(this.Package); Write(expr.Method.Class.Name); Write("::"); } WriteLowercaseWithUnderscores(expr.Method.Name); } else { // delegate call Write(expr.Obj); Write("->"); } WriteArguments(expr); } }