static bool HasSwitch(ICiStatement stmt) { if (stmt is CiSwitch) { return(true); } CiIf ifStmt = stmt as CiIf; if (ifStmt != null) { return(HasSwitch(ifStmt.OnTrue) || HasSwitch(ifStmt.OnFalse)); } CiLoop loop = stmt as CiLoop; if (loop != null) { return(HasSwitch(loop.Body)); } CiBlock block = stmt as CiBlock; if (block != null) { return(block.Statements.Any(s => HasSwitch(s))); } return(false); }
protected override void WriteLoopLabel(CiLoop stmt) { if (HasSwitchContinueAndEarlyBreak(stmt.Body)) { Write("LOOP: "); } }
void EndBody(CiLoop loop) { if (loop is CiFor forLoop) { if (forLoop.IsRange) { return; } forLoop.Advance?.Accept(this); } if (NeedCondXcrement(loop)) { VisitXcrement <CiPrefixExpr>(loop.Cond, true); } }
void CloseWhile(CiLoop loop) { CloseChild(); if (loop.Cond != null && VisitXcrement <CiPostfixExpr>(loop.Cond, false)) { if (loop.HasBreak) { Write("else"); OpenChild(); VisitXcrement <CiPostfixExpr>(loop.Cond, true); CloseChild(); } else { VisitXcrement <CiPostfixExpr>(loop.Cond, true); } } }
void CloseWhile(CiLoop loop) { loop.Body.Accept(this); if (loop.Body.CompletesNormally) { EndBody(loop); } CloseChild(); if (NeedCondXcrement(loop)) { if (loop.HasBreak && VisitXcrement <CiPostfixExpr>(loop.Cond, false)) { Write("else"); OpenChild(); VisitXcrement <CiPostfixExpr>(loop.Cond, true); CloseChild(); } else { VisitXcrement <CiPostfixExpr>(loop.Cond, true); } } }
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); } } } }
CiExpr ICiExprVisitor.Visit(CiSymbolAccess expr) { CiSymbol symbol = Lookup(expr); if (symbol is CiVar) { return new CiVarAccess { Var = (CiVar)symbol } } ; if (symbol is CiConst) { return(GetValue((CiConst)symbol)); } if (symbol is CiField) { if (this.CurrentMethod.CallType == CiCallType.Static) { throw new ResolveException("Cannot access field from a static method"); } symbol.Accept(this); return(CreateFieldAccess(new CiVarAccess { Var = this.CurrentMethod.This }, (CiField)symbol)); } throw new ResolveException("Invalid expression"); } CiExpr ICiExprVisitor.Visit(CiUnknownMemberAccess expr) { if (expr.Parent is CiSymbolAccess) { CiSymbol symbol = Lookup((CiSymbolAccess)expr.Parent); if (symbol is CiEnum) { return(new CiConstExpr(((CiEnum)symbol).LookupMember(expr.Name))); } if (symbol is CiClass) { symbol = ((CiClass)symbol).Members.Lookup(expr.Name); if (symbol is CiConst) { return(GetValue((CiConst)symbol)); } throw new ResolveException("Cannot access " + expr.Name); } } CiExpr parent = Resolve(expr.Parent); CiSymbol member = parent.Type.LookupMember(expr.Name); member.Accept(this); if (member is CiField) { return(CreateFieldAccess(parent, (CiField)member)); } if (member is CiProperty) { CiProperty prop = (CiProperty)member; if (parent is CiConstExpr) { if (prop == CiLibrary.LowByteProperty) { return(new CiConstExpr((byte)GetConstInt(parent))); } if (prop == CiLibrary.SByteProperty) { return(new CiConstExpr((int)(sbyte)GetConstInt(parent))); } if (prop == CiLibrary.StringLengthProperty) { return(new CiConstExpr(((string)((CiConstExpr)parent).Value).Length)); } } return(new CiPropertyAccess { Obj = parent, Property = prop }); } if (member is CiConst) { return(new CiConstExpr(((CiConst)member).Value)); } throw new ResolveException(member.ToString()); } CiExpr ICiExprVisitor.Visit(CiIndexAccess expr) { CiExpr parent = Resolve(expr.Parent); CiExpr index = Coerce(Resolve(expr.Index), CiIntType.Value); if (parent.Type is CiArrayType) { return new CiArrayAccess { Array = parent, Index = index } } ; if (parent.Type is CiStringType) { if (parent is CiConstExpr && index is CiConstExpr) { string s = (string)((CiConstExpr)parent).Value; int i = GetConstInt(index); if (i < s.Length) { return(new CiConstExpr((int)s[i])); } } return(new CiMethodCall { Method = CiLibrary.CharAtMethod, Obj = parent, Arguments = new CiExpr[1] { index } }); } throw new ResolveException("Indexed object is neither array or string"); } void ICiSymbolVisitor.Visit(CiDelegate del) { del.ReturnType = Resolve(del.ReturnType); foreach (CiParam param in del.Params) { param.Type = Resolve(param.Type); } } CiType ICiTypeVisitor.Visit(CiDelegate del) { ((ICiSymbolVisitor)this).Visit(del); return(del); } void ResolveObj(CiMethodCall expr) { if (expr.Obj is CiSymbolAccess) { // Foo(...) CiMethod method = Lookup((CiSymbolAccess)expr.Obj) as CiMethod; if (method != null) { expr.Method = method; if (method.CallType == CiCallType.Static) { expr.Obj = null; } else { if (this.CurrentMethod.CallType == CiCallType.Static) { throw new ResolveException("Cannot call instance method from a static method"); } expr.Obj = Coerce(new CiVarAccess { Var = this.CurrentMethod.This }, new CiClassPtrType { Class = method.Class }); CheckCopyPtr(method.This.Type, expr.Obj); } return; } } else if (expr.Obj is CiUnknownMemberAccess) { // ???.Foo(...) CiUnknownMemberAccess uma = (CiUnknownMemberAccess)expr.Obj; if (uma.Parent is CiSymbolAccess) { CiClass klass = Lookup((CiSymbolAccess)uma.Parent) as CiClass; if (klass != null) { // Class.Foo(...) CiMethod method = klass.Members.Lookup(uma.Name) as CiMethod; if (method != null) { if (method.CallType != CiCallType.Static) { throw new ResolveException("{0} is a non-static method", method.Name); } expr.Method = method; expr.Obj = null; return; } } } CiExpr obj = Resolve(uma.Parent); { CiMethod method = obj.Type.LookupMember(uma.Name) as CiMethod; if (method != null) { // obj.Foo(...) if (method.CallType == CiCallType.Static) { throw new ResolveException("{0} is a static method", method.Name); } if (method.This != null) { // user-defined method CheckCopyPtr(method.This.Type, obj); obj = Coerce(obj, new CiClassPtrType { Class = method.Class }); } expr.Method = method; expr.Obj = obj; return; } } } expr.Obj = Resolve(expr.Obj); if (!(expr.Obj.Type is CiDelegate)) { throw new ResolveException("Invalid call"); } if (expr.Obj.HasSideEffect) { throw new ResolveException("Side effects not allowed in delegate call"); } } void CoerceArguments(CiMethodCall expr) { expr.Signature.Accept(this); CiParam[] paramz = expr.Signature.Params; if (expr.Arguments.Length != paramz.Length) { throw new ResolveException("Invalid number of arguments for {0}, expected {1}, got {2}", expr.Signature.Name, paramz.Length, expr.Arguments.Length); } for (int i = 0; i < paramz.Length; i++) { CiExpr arg = Resolve(expr.Arguments[i]); CheckCopyPtr(paramz[i].Type, arg); expr.Arguments[i] = Coerce(arg, paramz[i].Type); } } CiExpr ICiExprVisitor.Visit(CiMethodCall expr) { ResolveObj(expr); CoerceArguments(expr); if (expr.Method != null && expr.Method != this.CurrentMethod) { if (expr.Method.IsMutator) { MarkWritable(expr.Obj); } expr.Method.CalledBy.Add(this.CurrentMethod); this.CurrentMethod.Calls.Add(expr.Method); } return(expr); } CiExpr ICiExprVisitor.Visit(CiUnaryExpr expr) { CiExpr resolved; if (expr.Op == CiToken.Increment || expr.Op == CiToken.Decrement) { resolved = ResolveLValue(expr.Inner); } else { resolved = Resolve(expr.Inner); } expr.Inner = Coerce(resolved, CiIntType.Value); if (expr.Op == CiToken.Minus && expr.Inner is CiConstExpr) { return(new CiConstExpr(-GetConstInt(expr.Inner))); } return(expr); } CiExpr ICiExprVisitor.Visit(CiCondNotExpr expr) { expr.Inner = Coerce(Resolve(expr.Inner), CiBoolType.Value); return(expr); } CiExpr ICiExprVisitor.Visit(CiPostfixExpr expr) { expr.Inner = Coerce(ResolveLValue(expr.Inner), CiIntType.Value); return(expr); } 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); } static CiType FindCommonType(CiExpr expr1, CiExpr expr2) { CiType type1 = expr1.Type; CiType type2 = expr2.Type; if (type1.Equals(type2)) { return(type1); } if ((type1 == CiIntType.Value && type2 == CiByteType.Value) || (type1 == CiByteType.Value && type2 == CiIntType.Value)) { return(CiIntType.Value); } CiType type = type1.Ptr; if (type != null) { return(type); // stg, ptr || stg, null } type = type2.Ptr; if (type != null) { return(type); // ptr, stg || null, stg } if (type1 != CiType.Null) { return(type1); // ptr, null } if (type2 != CiType.Null) { return(type2); // null, ptr } throw new ResolveException("Incompatible types"); } CiExpr ICiExprVisitor.Visit(CiBoolBinaryExpr expr) { CiExpr left = Resolve(expr.Left); CiExpr right = Resolve(expr.Right); CiType type; switch (expr.Op) { case CiToken.CondAnd: case CiToken.CondOr: type = CiBoolType.Value; break; case CiToken.Equal: case CiToken.NotEqual: type = FindCommonType(left, right); break; default: type = CiIntType.Value; break; } expr.Left = Coerce(left, type); expr.Right = Coerce(right, type); CiConstExpr cleft = expr.Left as CiConstExpr; if (cleft != null) { switch (expr.Op) { case CiToken.CondAnd: return((bool)cleft.Value ? expr.Right : new CiConstExpr(false)); case CiToken.CondOr: return((bool)cleft.Value ? new CiConstExpr(true) : expr.Right); case CiToken.Equal: case CiToken.NotEqual: CiConstExpr cright = expr.Right as CiConstExpr; if (cright != null) { bool eq = object.Equals(cleft.Value, cright.Value); return(new CiConstExpr(expr.Op == CiToken.Equal ? eq : !eq)); } break; default: if (expr.Right is CiConstExpr) { int a = GetConstInt(cleft); int b = GetConstInt(expr.Right); bool result; switch (expr.Op) { case CiToken.Less: result = a < b; break; case CiToken.LessOrEqual: result = a <= b; break; case CiToken.Greater: result = a > b; break; case CiToken.GreaterOrEqual: result = a >= b; break; default: return(expr); } return(new CiConstExpr(result)); } break; } } return(expr); } CiExpr ICiExprVisitor.Visit(CiCondExpr expr) { expr.Cond = Coerce(Resolve(expr.Cond), CiBoolType.Value); CiExpr expr1 = Resolve(expr.OnTrue); CiExpr expr2 = Resolve(expr.OnFalse); expr.ResultType = FindCommonType(expr1, expr2); expr.OnTrue = Coerce(expr1, expr.ResultType); expr.OnFalse = Coerce(expr2, expr.ResultType); CiConstExpr konst = expr.Cond as CiConstExpr; if (konst != null) { return((bool)konst.Value ? expr.OnTrue : expr.OnFalse); } return(expr); } CiExpr ICiExprVisitor.Visit(CiBinaryResourceExpr expr) { string name = (string)ResolveConstExpr(expr.NameExpr, CiStringPtrType.Value); CiBinaryResource resource; if (!this.BinaryResources.TryGetValue(name, out resource)) { resource = new CiBinaryResource(); resource.Name = name; resource.Content = File.ReadAllBytes(FindFile(name)); resource.Type = new CiArrayStorageType { ElementType = CiByteType.Value, Length = resource.Content.Length }; this.BinaryResources.Add(name, resource); } expr.Resource = resource; return(expr); } CiExpr ICiExprVisitor.Visit(CiNewExpr expr) { CiType type = expr.NewType; CiClassStorageType classStorageType = type as CiClassStorageType; if (classStorageType != null) { classStorageType.Class = ResolveClass(classStorageType.Class); classStorageType.Class.IsAllocated = true; } else { CiArrayStorageType arrayStorageType = (CiArrayStorageType)type; arrayStorageType.ElementType = Resolve(arrayStorageType.ElementType); arrayStorageType.LengthExpr = Coerce(Resolve(arrayStorageType.LengthExpr), CiIntType.Value); } return(expr); } CiExpr Resolve(CiExpr expr) { return(expr.Accept(this)); } void ICiSymbolVisitor.Visit(CiField field) { field.Type = Resolve(field.Type); } bool Resolve(ICiStatement[] statements) { bool reachable = true; foreach (ICiStatement child in statements) { if (!reachable) { throw new ResolveException("Unreachable statement"); } child.Accept(this); reachable = child.CompletesNormally; } return(reachable); } void ICiStatementVisitor.Visit(CiBlock statement) { statement.CompletesNormally = Resolve(statement.Statements); } void ICiStatementVisitor.Visit(CiConst statement) { } void ICiStatementVisitor.Visit(CiVar statement) { statement.Type = Resolve(statement.Type); if (statement.InitialValue != null) { CiType type = statement.Type; CiExpr initialValue = Resolve(statement.InitialValue); CheckCopyPtr(type, initialValue); if (type is CiArrayStorageType) { type = ((CiArrayStorageType)type).ElementType; CiConstExpr ce = Coerce(initialValue, type) as CiConstExpr; if (ce == null) { throw new ResolveException("Array initializer is not constant"); } statement.InitialValue = ce; if (type == CiBoolType.Value) { if (!false.Equals(ce.Value)) { throw new ResolveException("Bool arrays can only be initialized with false"); } } else if (type == CiByteType.Value) { if (!((byte)0).Equals(ce.Value)) { throw new ResolveException("Byte arrays can only be initialized with zero"); } } else if (type == CiIntType.Value) { if (!0.Equals(ce.Value)) { throw new ResolveException("Int arrays can only be initialized with zero"); } } else { throw new ResolveException("Invalid array initializer"); } } else { statement.InitialValue = Coerce(initialValue, type); } } } void ICiStatementVisitor.Visit(CiExpr statement) { Resolve((CiExpr)statement); } void ICiStatementVisitor.Visit(CiAssign statement) { statement.Target = ResolveLValue(statement.Target); if (statement.Target is CiVarAccess && ((CiVarAccess)statement.Target).Var == this.CurrentMethod.This) { throw new ResolveException("Cannot assign to this"); } CiMaybeAssign source = statement.Source; if (source is CiAssign) { Resolve((ICiStatement)source); } else { source = Resolve((CiExpr)source); } CiType type = statement.Target.Type; CheckCopyPtr(type, source); statement.Source = Coerce(source, type); if (statement.Op != CiToken.Assign && type != CiIntType.Value && type != CiByteType.Value) { if (statement.Op == CiToken.AddAssign && type is CiStringStorageType && statement.Source.Type is CiStringType) { } // OK else { throw new ResolveException("Invalid compound assignment"); } } } void ICiStatementVisitor.Visit(CiDelete statement) { statement.Expr = Resolve(statement.Expr); ICiPtrType type = statement.Expr.Type as ICiPtrType; if (type == null) { throw new ResolveException("'delete' takes a class or array pointer"); } if (statement.Expr.HasSideEffect) { throw new ResolveException("Side effects not allowed in 'delete'"); } this.WritablePtrTypes.Add(type); } void ICiStatementVisitor.Visit(CiBreak statement) { if (this.CurrentLoopOrSwitch == null) { throw new ResolveException("break outside loop and switch"); } this.CurrentLoopOrSwitch.CompletesNormally = true; } void ICiStatementVisitor.Visit(CiContinue statement) { if (this.CurrentLoop == null) { throw new ResolveException("continue outside loop"); } } void ResolveLoop(CiLoop statement) { statement.CompletesNormally = false; if (statement.Cond != null) { statement.Cond = Coerce(Resolve(statement.Cond), CiBoolType.Value); statement.CompletesNormally = !statement.Cond.IsConst(false); } CiLoop oldLoop = this.CurrentLoop; CiCondCompletionStatement oldLoopOrSwitch = this.CurrentLoopOrSwitch; this.CurrentLoopOrSwitch = this.CurrentLoop = statement; Resolve(statement.Body); this.CurrentLoop = oldLoop; this.CurrentLoopOrSwitch = oldLoopOrSwitch; } void ICiStatementVisitor.Visit(CiDoWhile statement) { ResolveLoop(statement); } void ICiStatementVisitor.Visit(CiFor statement) { if (statement.Init != null) { Resolve(statement.Init); } if (statement.Advance != null) { Resolve(statement.Advance); } ResolveLoop(statement); } void ICiStatementVisitor.Visit(CiIf statement) { statement.Cond = Coerce(Resolve(statement.Cond), CiBoolType.Value); Resolve(statement.OnTrue); if (statement.OnFalse != null) { Resolve(statement.OnFalse); statement.CompletesNormally = statement.OnTrue.CompletesNormally || statement.OnFalse.CompletesNormally; } else { statement.CompletesNormally = true; } } void ICiStatementVisitor.Visit(CiNativeBlock statement) { } void ICiStatementVisitor.Visit(CiReturn statement) { CiType type = this.CurrentMethod.Signature.ReturnType; if (type != CiType.Void) { statement.Value = Coerce(Resolve(statement.Value), type); } } void ICiStatementVisitor.Visit(CiSwitch statement) { statement.Value = Resolve(statement.Value); CiType type = statement.Value.Type; CiCondCompletionStatement oldLoopOrSwitch = this.CurrentLoopOrSwitch; this.CurrentLoopOrSwitch = statement; HashSet <object> values = new HashSet <object>(); CiCase fallthroughFrom = null; foreach (CiCase kase in statement.Cases) { for (int i = 0; i < kase.Values.Length; i++) { kase.Values[i] = ResolveConstExpr((CiExpr)kase.Values[i], type); if (!values.Add(kase.Values[i])) { throw new ResolveException("Duplicate case value"); } } if (fallthroughFrom != null) { if (fallthroughFrom.FallthroughTo == null) { throw new ResolveException("goto default followed by case"); } if (!ResolveConstExpr(fallthroughFrom.FallthroughTo, type).Equals(kase.Values[0])) { throw new ResolveException("goto case doesn't match the next case"); } } bool reachable = Resolve(kase.Body); if (kase.Fallthrough) { if (!reachable) { throw new ResolveException("goto is not reachable"); } fallthroughFrom = kase; } else { if (reachable) { throw new ResolveException("case must end with break, return, throw or goto"); } fallthroughFrom = null; } } if (statement.DefaultBody != null) { if (fallthroughFrom != null && fallthroughFrom.FallthroughTo != null) { throw new ResolveException("goto case followed by default"); } bool reachable = Resolve(statement.DefaultBody); if (reachable) { throw new ResolveException("default must end with break, return, throw or goto"); } } else { if (fallthroughFrom != null) { throw new ResolveException("goto cannot be the last statement in switch"); } } this.CurrentLoopOrSwitch = oldLoopOrSwitch; } void ICiStatementVisitor.Visit(CiThrow statement) { statement.Message = Coerce(Resolve(statement.Message), CiStringPtrType.Value); this.ThrowingMethods.Add(this.CurrentMethod); } void ICiStatementVisitor.Visit(CiWhile statement) { ResolveLoop(statement); } void Resolve(ICiStatement statement) { statement.Accept(this); } void ICiSymbolVisitor.Visit(CiMethod method) { this.CurrentMethod = method; Resolve(method.Signature); if (method.CallType != CiCallType.Abstract) { Resolve(method.Body); if (method.Signature.ReturnType != CiType.Void && method.Body.CompletesNormally) { throw new ResolveException("Method can complete without a return value"); } } this.CurrentMethod = null; } void ResolveBase(CiClass klass) { if (klass.BaseClass != null) { klass.BaseClass = ResolveClass(klass.BaseClass); klass.Members.Parent = klass.BaseClass.Members; } } void ICiSymbolVisitor.Visit(CiClass klass) { this.CurrentClass = klass; this.Symbols = klass.Members; if (klass.Constructor != null) { klass.Constructor.Accept(this); } foreach (CiSymbol member in klass.Members) { member.Accept(this); } klass.BinaryResources = this.BinaryResources.Values.ToArray(); this.BinaryResources.Clear(); this.Symbols = this.Symbols.Parent; this.CurrentClass = null; }
void ResolveLoop(CiLoop statement) { statement.CompletesNormally = false; if (statement.Cond != null) { statement.Cond = Coerce(Resolve(statement.Cond), CiBoolType.Value); statement.CompletesNormally = !statement.Cond.IsConst(false); } CiLoop oldLoop = this.CurrentLoop; CiCondCompletionStatement oldLoopOrSwitch = this.CurrentLoopOrSwitch; this.CurrentLoopOrSwitch = this.CurrentLoop = statement; Resolve(statement.Body); this.CurrentLoop = oldLoop; this.CurrentLoopOrSwitch = oldLoopOrSwitch; }
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); } } } }
protected virtual void OpenWhile(CiLoop loop) { OpenCond("while ", loop.Cond, CiPriority.Argument); }
protected virtual bool NeedCondXcrement(CiLoop loop) => loop.Cond != null;
protected virtual void WriteLoopLabel(CiLoop stmt) { }
public CiContinue(CiLoop loop) { this.Loop = loop; }
protected override void WriteLoopLabel(CiLoop stmt) { if (HasSwitchContinueAndEarlyBreak(stmt.Body)) Write("LOOP: "); }