protected OptionValue GetOption(NodeListParser nlp) { if (nlp.IsNumber) { return(OptionValue.Create(nlp.GetNumber().Value)); } if (nlp.IsBool) { return(OptionValue.Create(nlp.GetBool().Value ? 1 : 0)); } // note that options are case insensitive if (nlp.IsIdent && _optionlookup.ContainsKey(nlp.CurrentIdent.Name.ToLower())) { return(_optionlookup[nlp.GetIdent().Name.ToLower()]); } nlp.Expected("number or option keyword"); return(OptionValue.Default); }
PositionOrZone ParsePositionOrZone(NodeListParser nlp) { if (nlp.IsConstant) { var value = nlp.GetValue(); if (value is PositionValue || value is ZoneValue) { return new PositionOrZone { Value = value } } ; else { } } nlp.Expected("position or zone"); return(null); }
OccupierDef ParseOccupier(NodeListParser nlp) { //NOTE: this change allows dropping parens, but breaks order dependence //if (nlp.IsIdent && (nlp.CurrentIdent.IsPiece || nlp.CurrentIdent.IsDirection)) { // take ident; define as piece if not direction if (nlp.IsIdent) { var ident = nlp.GetIdent(); if (ident.IsDirection) // only used by relative-config { return(OccupierDef.Create(ident.AsValue as DirectionValue)); } else { return(OccupierDef.Create(PlayerKinds.Friend, DefPiece(ident))); } } // note: relies on dummy functions defined in scope if (nlp.IsList && nlp.IsCallable) { var nlp2 = nlp.GetParser(); var ident = nlp2.GetIdent(); if (ident.Sym.Name == "not") { return(ParseOccupier(nlp2).SetNot()); } else { var piece = DefPiece(nlp2.GetIdent()); nlp2.CheckDone(); if (ident.Sym.Name == "opponent") { return(OccupierDef.Create(PlayerKinds.Enemy, piece)); } if (ident.Sym.Name == "any-owner") { return(OccupierDef.Create(PlayerKinds.Any, piece)); } } } return(null); }
// Compile a scoped expression that returns a typed value DataTypes CompileExpr(PredefScopes predef, NodeListParser nlp) { Logger.WriteLine(2, "CompileExpr {0} <{1}>", predef, nlp); Symbols.PushPredefScope(predef); var codetype = Symbols.PredefScopeDict[predef].CodeType; _gen.EmitEntry(codetype); nlp.Expect(nlp.IsCallable, "callable function"); var sexpr = nlp.GetSexprNode(); nlp.Expect(sexpr.DataType != DataTypes.Void, "typed function"); CompileSexpr(sexpr, nlp); Symbols.PopPredefScope(); _gen.EmitExit(true); return(sexpr.DataType); }
// Compile top level scoped program block, scoped expression or variant merge private void CompileProg(BuiltinScopeInfo info, NodeListParser nlp) { if (info.Kind == PredefKinds.PROGM) { CompileGameOrVariant(info.Predef, nlp); } else if (info.Kind == PredefKinds.PROG) { CompileProg(info.Predef, nlp); } else if (info.Kind == PredefKinds.EXPR) { CompileExpr(info.Predef, nlp); } else { throw Error.Assert("{0}", info.Kind); } }
//void CompileValue(Type type, NodeListParser nlp) { // Logger.WriteLine(4, "CompileValue {0} <{1}>", type, nlp); // var func = _typeactiondict.SafeLookup(type); // // bracketed expression with arguments // if (nlp.IsSexpr) { // var sexpr = nlp.GetSexprNode(); // var datatype = TypedValue.DataTypeDict.SafeLookup(type); // nlp.Expect(sexpr.DataType == datatype, "value of type {0}", datatype); // CompileSexpr(sexpr, nlp); // } else if (nlp.IsFunc) { // // bare function no arguments // var sym = nlp.GetIdent().Sym as BuiltinSymbol; // CompileBuiltin(sym, NodeListParser.Null); // } else if (nlp.IsVariable) { // var sym = nlp.GetIdent().Sym; // _gen.EmitLoadVar(sym); // } else if (nlp.IsAttribute || (nlp.IsList && nlp.PeekList.IsAttribute)) { // var handler = Symbols.Find("--attribute") as BuiltinSymbol; // if (handler != null) // CompileBuiltin(handler, nlp.GetParser()); // else nlp.Syntax("attribute not allowed here"); // } else if (func != null) { // // direct lookup to get constant value // var value = func(type, nlp, this); // _gen.EmitLoadValue(value); // } else nlp.Unexpected("unknown type {0}", type); //} //-------------------------------------------------------------------------- // Compile control constructs that generate GOTOs and emit custom code void CompileIf(Symbol sym, NodeListParser nlp) { CompileArg(typeof(BoolValue), nlp); var pos1 = _gen.Counter; _gen.EmitGoFalse(0); CompileProg(nlp); // will return without taking else if (nlp.IsCallable && nlp.CurrentIdent.Sym.Keyword == Keywords.ELSE) { nlp.GetSexprNode(); var pos2 = _gen.Counter; _gen.EmitGoTo(0); _gen.Fixup(pos1); CompileProg(nlp); _gen.Fixup(pos2); } else { _gen.Fixup(pos1); } }
internal void CompileProg(PredefScopes predef, NodeListParser nlp) { Logger.WriteLine(2, "CompileProg {0} <{1}>", predef, nlp); _currentparser = nlp; Symbols.PushPredefScope(predef); if (predef == PredefScopes.GAME) { Symbols.CurrentScope.Push(); } var codetype = Symbols.PredefScopeDict[predef].CodeType; _gen.EmitEntry(codetype); CompileProg(nlp); _gen.EmitExit(false); Symbols.PopPredefScope(); if (predef == PredefScopes.GAME) { Symbols.CurrentScope.Pop(); } }
PlacementDef ParsePlacement(NodeListParser nlp) { var off = 0; var poslist = new List <PositionValue>(); var piece = DefPiece(nlp.GetIdent()); while (!nlp.Done) { var ident = nlp.GetIdent(); if (ident.Name == "off") { off = nlp.GetInt(); } else { poslist.Add(DefPosition(ident)); } } return(new PlacementDef { Piece = piece, OffQuantity = off, Positions = poslist }); }
//-------------------------------------------------------------------------- // Parsers for individual productions that return a data structure //: (TURN-ORDER { player | ( player move-type ) | ( player player ) | ( player player move-type ) | REPEAT }+ )? TurnDef ParseTurnDef(NodeListParser nlparg) { Logger.WriteLine(4, "ParseTurnDef <{0}>", nlparg); nlparg.Expect(nlparg.IsIdent || nlparg.IsList, "player or list"); var nlp = nlparg.GetParser(); var ident = nlp.GetIdent(); if (ident.Name == "repeat") { nlp.CheckDone(); return(TurnDef.Repeat); } var player = DefPlayer(ident); // default is to play as self var playeras = nlp.IsIdent && nlp.Current.IsPlayer ? DefPlayer(nlp.GetIdent()) : player; var movetype = nlp.IsIdent ? DefMoveType(nlp.GetIdent()) : MoveTypeValue.Any; nlp.CheckDone(); return(new TurnDef { TurnPlayer = player, MovePlayer = playeras, MoveType = movetype }); }
// Compile and merge nodes from base game and variant internal void CompileGameOrVariant(PredefScopes predef, NodeListParser nlp) { Logger.WriteLine(1, "CompileGameOrVariant {0} <{1}>", predef, nlp); Symbols.PushPredefScope(predef); Symbols.CurrentScope.Push(); // game index will define pieces if (_game_index == null) { _game_index = new GameIndex(); _currentparser = NodeListParser.Create(_game_index.IndexGame(nlp), nlp.Symbols); } else { _currentparser = NodeListParser.Create(_game_index.MergeVariant(nlp), nlp.Symbols); } var codetype = Symbols.PredefScopeDict[predef].CodeType; _gen.EmitEntry(codetype); CompileProg(_currentparser); _gen.EmitExit(false); Symbols.CurrentScope.Pop(); Symbols.PopPredefScope(); }
// merge game and variant to create new game internal IList <Node> MergeVariant(NodeListParser nlp) { // Rule 1. Remove all piece nodes -- special handling at end var plookup = CreatePieceLookup(nlp); var vnodes = nlp.Nodes.Where(n => name(n) != "piece").ToList(); // Rule 2. If variant has any conditions, remove all game conditions var hascond = vnodes.Any(v => condlookup.Contains(name(v))); var gnodesng = (hascond) ? _gnodes.Where(n => !condlookup.Contains(name(n))) : _gnodes; // Rule 3. For each game node, replace with matching variant node if any // Note: must use each variant node only once; preserves order of input file var newnodes = new List <Node>(); foreach (var n in gnodesng) { var i = vnodes.FindIndex(v => name(n) == name(v)); if (i != -1) { newnodes.Add(vnodes[i]); vnodes.RemoveAt(i); } else { newnodes.Add(n); } } // Rule 3. Append any unmatched variant nodes to the end, followed by pieces newnodes.AddRange(vnodes); newnodes.AddRange(GetPieceList(plookup)); // Logger.WriteLine(3, "Merge {0} '{1}' '{2}'", hascond, vnodes.Select(n => name(n)).Join(), // newnodes.Select(n => name(n)).Join()); return(newnodes); }
// Compile a function with given arguments void CompileBuiltin(BuiltinSymbol funcsym, NodeListParser nlp) { Logger.WriteLine(3, "CompileBuiltin {0} <{1}>", funcsym.Name, nlp); var ci = funcsym.CallInfo; for (int i = 0; i < ci.Arguments.Count; i++) { if (ci.ArgOptional[i] && nlp.Done) { _gen.EmitLoadValue(null); } else if (ci.Arguments[i] == typeof(AttributeValue)) { _gen.EmitLoadValue(DefAttribute(nlp.GetIdent())); // special for attribute } // TODO: could possibly check for combo of boolean expected and undefined ident, and emit false else { CompileArg(ci.Arguments[i], nlp); } } nlp.Expect(nlp.Done, "no more arguments for {0}", funcsym.Name); _gen.EmitCall(funcsym); }
// Compile an argument of given type void CompileArg(Type type, NodeListParser nlp) { Logger.WriteLine(4, "CompileArg {0} <{1}>", type, nlp); var func = _typeactiondict.SafeLookup(type); // special for variable assignment and implicit definition if (type == typeof(Variable)) { var ident = nlp.GetIdent(); var datatype = DataTypes.Bool; // TODO: handle other types if (ident.Sym.IsVariable) { nlp.Expect(ident.Sym.DataType == datatype, "variable of type {0}", datatype); } else if (ident.Sym.IsUndef) { Symbols.DefineVariable(ident.Name, datatype, BoolValue.False); } else { nlp.Expected("defined variable"); } _gen.EmitRefVar(ident.Sym); // handle these separately, could be variable or function } else if (type.IsSubclassOf(typeof(TypedValue))) { // TODO: type checking CompileValue(type, nlp); //var exptype = TypedValue.DataTypeDict.SafeLookup(type); //var rettype = CompileValue(type, nlp); //nlp.Expect(rettype == exptype, "value of type {0}", exptype); // direct lookup gets special cases; else continue } else if (func != null) { var value = func(type, nlp, this); _gen.EmitLoadValue(value); // Array means parse a list of this type } else if (type.IsArray) { var nargs = 0; for (var nlp2 = nlp.GetParser(); !nlp2.Done; nargs++) { CompileArg(type.GetElementType(), nlp2); } _gen.EmitToArray(type, nargs); // List<> means parse a tail of this type } else if (type.IsGenericType && type.Name.StartsWith("List")) { var nargs = 0; for (; !nlp.Done; ++nargs) { CompileArg(type.GetGenericArguments()[0], nlp); } _gen.EmitToList(type, nargs); // Pair<> means parse a pair of arbitrary types } else if (type.IsGenericType && type.Name.StartsWith("Pair")) { var nlp2 = nlp.GetParser(); foreach (var subtype in type.GetGenericArguments()) { CompileArg(subtype, nlp2); } _gen.EmitToPair(type); // nested prog block } else if (type.IsSubclassOf(typeof(CodeBase))) { var info = Symbols.PredefScopeDict.First(kv => kv.Value.CodeType == type).Value; CompileProg(info, nlp); } else if (type == typeof(PositionOrDirection)) { CompilePositionOrDirection(nlp); // function call on built in } else if (nlp.IsSexpr) { var sexpr = nlp.GetSexprNode(); var datatype = TypedValue.DataTypeDict.SafeLookup(type); nlp.Expect(sexpr.DataType == datatype, "value of type {0}", datatype); CompileBuiltin(sexpr.Sym as BuiltinSymbol, NodeListParser.Create(sexpr.Args, Symbols)); } else { nlp.Syntax("unknown type {0}", type); } }
// main entry point, parses all the variants and returns a list internal void CompileMenu(IList <Node> nodes) { Logger.WriteLine(1, "CompileMenu {0} nodes", nodes.Count); CompileProg(PredefScopes.MENU, NodeListParser.Create(nodes, Symbols)); }
Maybe <PositionOrZone> ParseMaybePositionOrZone(NodeListParser nlp) { if (nlp.IsConstant) { var value = nlp.Current.AsValue; if (value is PositionValue || value is ZoneValue) { return new Maybe <PositionOrZone> { Value = ParsePositionOrZone(nlp) } } ; } return(new Maybe <PositionOrZone> { }); } // parse a term which may be a position, direction or opposite void CompilePositionOrDirection(NodeListParser nlp) { var rettype = CompileValue(typeof(TypedValue), nlp); nlp.Expect(rettype == DataTypes.Position || rettype == DataTypes.Direction, "position or direction"); var funcsym = Symbols.Find("--pos-or-dir"); _gen.EmitCall(funcsym as BuiltinSymbol); } //protected PositionOrDirection ParsePositionOrDirection(NodeListParser nlp) { // if (nlp.IsValue) { // var value = nlp.GetValue(); // if (value is PositionValue || value is DirectionValue) // return new PositionOrDirection { Value = value }; // } // nlp.Expected("position or direction"); // return null; //} GoKinds ParseGoKind(NodeListParser nlp) { var ident = nlp.GetIdent(); var gosym = Symbols.Find(ident.Name, PredefKinds.GO); nlp.Expect(gosym != null, "valid go target"); return(gosym.GoKind); } Maybe <PlayerValue[]> ParseMaybePlayers(NodeListParser nlp) { if (!(nlp.IsList && nlp.CheckedHead.IsPlayer)) { return(Maybe <PlayerValue[]> .Null); } return(Maybe <PlayerValue[]> .Create(nlp.GetParser().UntilDone(n => DefPlayer(n.GetIdent())).ToArray())); } Maybe <PieceValue> ParseMaybePiece(NodeListParser nlp) { return((nlp.IsIdent && nlp.Current.IsPiece) ? Maybe <PieceValue> .Create(DefPiece(nlp.GetIdent())) : Maybe <PieceValue> .Null); } Maybe <PlayerValue> ParseMaybePlayer(NodeListParser nlp) { return((nlp.IsIdent && nlp.Current.IsPlayer) ? Maybe <PlayerValue> .Create(DefPlayer(nlp.GetIdent())) : Maybe <PlayerValue> .Null); } }