/// <summary> /// /// </summary> /// <returns></returns> public void ScanInnerSequence(Sequence sequence) { while (_offset < _source.Length) { if (_token.type == TokenType.String) { sequence.Params.Add(new Constant(_token.value)); LexTag(); } else if (_token.type == TokenType.Integer) { sequence.Params.Add(new Constant(_token.value.ToInt().Value)); LexTag(); } else if (_token.type == TokenType.Decimal) { sequence.Params.Add(new Constant(_token.value.ToInt().Value)); LexTag(); } else if (_token.type == TokenType.Identifier) { var identifier = _token.value; LexTag(); if (_token.type == TokenType.Separator) { sequence.Params.Add(new Get(new Constant(identifier))); } else if (_token.type == TokenType.ArgumentOpen) { LexTag(); //todo: multiple arguments eventually var argumentSequence = ScanBaseSequence(); if (_token.type != TokenType.ArgumentClose) { ErrorAction("Expecting )"); return; } var action = ScanAction(identifier, argumentSequence); if (action != null) sequence.Params.Add(action); // for the moment any methods are the end of a sequence //TODO: only Basic actions should be able to continue if (_token.type == TokenType.EOF || _token.type == TokenType.TagClose || _token.type == TokenType.ArgumentClose) return; } else if (_token.type == TokenType.Separator || _token.type == TokenType.TagClose) { // identifier was an access sequence.Params.Add(new Get(new Constant(identifier))); } } else if (_token.type == TokenType.Newline) return; else { ErrorAction("Unknown initial sequence value"); return; } if (_token.type != TokenType.Separator) return; LexTag(); } }
/// <summary> /// Enter with current token the first item to look at /// Returns with EOF, or TagClose /// </summary> /// <returns></returns> public IAction ScanBaseSequence() { var sequence = new Sequence(); if (_token.type == TokenType.ArgumentClose) return sequence; else if (_token.type == TokenType.Identifier) sequence.Params.Add(new Get(new Constant(_token.value))); else if (_token.type == TokenType.String) sequence.Params.Add(new Constant(_token.value)); else if (_token.type == TokenType.Integer) sequence.Params.Add(new Constant(_token.value.ToInt().Value)); else if (_token.type == TokenType.Decimal) sequence.Params.Add(new Constant(_token.value.ToInt().Value)); else if (_token.type == TokenType.Newline) return null; else { ErrorAction("Unknown initial sequence value"); return null; } LexTag(); if (_token.type == TokenType.Separator) { LexTag(); ScanInnerSequence(sequence); } if (_token.type == TokenType.EOF || _token.type == TokenType.TagClose || _token.type == TokenType.ArgumentClose) return sequence; if (_token.type == TokenType.Newline) return null; ErrorAction("Expecting end of file (maybe) or ) or \"}\""); return null; }
/// <summary> /// Enter with offset on the first character of the inner /// Leaves the token as EOF, or TagEnd /// </summary> /// <returns></returns> public IAction ScanInner() { var aggregate = new Aggregate(); var start = _offset; while (_offset < _source.Length) { // stops for the end of file or { ForwardTag(); if (_offset >= _source.Length) { aggregate.Params.Add( new Sequence(new List<IAction>() { new Constant(_source.Substring(start)), new Render() })); return aggregate; } // any content we find is render content var contentSequence = new Sequence(new List<IAction>() { new Constant(_source.Substring(start, _offset - start)), new Render() }); LexTag(); // returning now might be okay if we're running inner only // it's up to the caller to make sure it ended correctly if (_token.type == TokenType.EOF) { aggregate.Params.Add(contentSequence); return aggregate; } if (_token.type != TokenType.TagOpen) { ErrorAction("Expecting {"); return null; } LexTag(); if (_token.type == TokenType.TagEnd) { aggregate.Params.Add(contentSequence); return aggregate; } var sequence = ScanBaseSequence(); // newline found - must be javascript, ignore if (_token.type == TokenType.Newline) continue; if (sequence == null) return null; if (_token.type != TokenType.TagClose) { ErrorAction("Expecting \"}\""); return null; } var lastAction = sequence.Params.Last(); if (!(lastAction is Each || lastAction is ICondition)) sequence.Params.Add(new Render()); aggregate.Params.Add(contentSequence); aggregate.Params.Add(sequence); start = _offset; } return aggregate; }
public IAction Scan() { if (_source == null) using (var sr = new StreamReader(FileSystem.GetFile(_location).Name)) _source = sr.ReadToEnd(); var aggregate = new Aggregate(); while (_offset < _source.Length) { // stops for the end of file or { ForwardTag(); if (_offset >= _source.Length) return aggregate; _offset++; var sequence = new Sequence(); LexTag(); if (_token.type == TokenType.Identifier) sequence.Params.Add(new Constant(_token.value)); else if (_token.type == TokenType.String) sequence.Params.Add(new Constant(_token.value)); else { ErrorAction("Expecting a value (variable location, string, or number)"); return null; } LexTag(); if (_token.type != TokenType.Separator) { ErrorAction("Expecting a method name (this should be something like .Assign())"); return null; } LexTag(); if (_token.type != TokenType.Identifier) { ErrorAction("An operation must be defined (this should be something like .Assign())"); return null; } var functionName = _token.value; LexTag(); if (_token.type != TokenType.ArgumentOpen) { ErrorAction("Expecting \"(\""); return null; } LexTag(); if (_token.type != TokenType.ArgumentClose) { Console.WriteLine("Expecting \")\""); Console.WriteLine(PrintToken('!')); return null; } LexTag(); if (_token.type == TokenType.EOF) { ErrorAction("Reached the end of file too soon, expecting more content"); return null; } if (_token.type == TokenType.Error) { ErrorAction(_token.value); return null; } //Newline found within tag (skipping tag) if (_token.type == TokenType.Newline) continue; // _token.type should always equal TokenType.TagClose at this point if (_token.type != TokenType.TagClose) throw new Exception("Unknown State\n" + PrintToken('!')); if (functionName == "Import") { if (!File.Exists(_location)) { ErrorAction("Import statement used and current location does not exist: " + _location); return null; } if (!(sequence.Params[0] is Constant)) { ErrorAction("Import statement requires a constant parameter"); return null; } // strip the filename off our path var currentFile = new FileInfo(_location); var pathToImport = ((Constant)sequence.Params[0]).Value.ToString(); var newPath = Path.Combine(currentFile.Directory.FullName, pathToImport); // lex it var lexer = new Lexer(newPath); var result = lexer.Scan(); aggregate.Params.AddRange(result.Params); continue; } var inner = ScanInner(); if (inner == null) return null; LexTag(); if (_token.type != TokenType.Identifier || _token.value != functionName) { ErrorAction("Expecting {/" + functionName + "}"); return null; } var func = ActionHandlers .Where(p => p.Key.Type == ActionType.External && p.Key.Identifier == functionName && p.Key.HasParameter == false) .Select(p => p.Value) .FirstOrNull(); if (func == null) { ErrorAction("Unknown initial function"); return null; } var action = (IBlock)func(); action.Content = inner; sequence.Params.Add(action); LexTag(); if (_token.type != TokenType.TagClose) { ErrorAction("Expecting \"}\""); return null; } aggregate.Params.Add(sequence); } return aggregate; }