public FunctionNode HandleFunction() { //get the function name SkipWhiteSpace(); Lexeme functionNameLexeme = Pop(LexemeType.WORD); FunctionNode functionNode = new FunctionNode(functionNameLexeme); string functionName = functionNameLexeme.text; //If the function only takes one argument, stop here if (!subroutineDatabase.TryGetValue(functionName, out SubroutineInformation subroutineInformation)) { throw GetParsingException($"Function {functionName} not in function database"); } //If have reached the end of line/nothing left to parse, just return no arguments if (!HasCurrent()) { return(functionNode); } //If there is a comma after the function name, just ignore it if (Peek().type == LexemeType.COMMA) { Pop(); } SkipWhiteSpace(); //Console.WriteLine($"Parsing function {functionName} which has {subroutineInformation.hasArguments} arguments"); if (subroutineInformation.hasArguments) { //parse the first argument if it has more than one argument //TODO: should proabably group tokens here rather than doing later? not sure.... functionNode.AddArgument(HandleExpression()); while (HasCurrent()) { //Just assume there is one argument after each comma //If anything else is found besides a comma, assume function arguments have ended. //TODO: (the engine will actually accept spaces instead of commas) //TODO: perhaps should use colon ':' to determine function end if more than one function after each other? SkipWhiteSpace(); if (Peek().type != LexemeType.COMMA) { break; } else { Pop(); } SkipWhiteSpace(); functionNode.AddArgument(HandleExpression()); } } return(functionNode); }
public void LexSection(bool sectionAllowsText) { bool mustBeExpression = false; bool firstIteration = true; while (HasCurrent()) { bool nextMustBeExpression = false; TryPopRegex(Regexes.whitespace, LexemeType.WHITESPACE); if (!HasCurrent()) { break; } if (!mustBeExpression && sectionAllowsText && (Peek() == '^' || Regexes.exclamationTextCommand.IsMatch(this.line, this.pos) || Peek() > 127)) { //only allow entering text mode if: // - not followed by a comma, as that would imply the next lexeme is a function argument which cannot be text mode // EXAMPLE: dwave_eng 0, ev2_3e816 ^ Chiester Sisters!!^@: // - not followed by an operator, as that would imply the next lexeme is a expression // Example: dwave_eng 0, ev2_3e816 + ^ Chiester Sisters!!^@: // - not followed by a function name which takes arguments, as that would imply it's meant to be // Example where it is allowed: langen ^She jumped and jumped and leapt and even flipped in midair, increasing that distance.^\ // Example where it NOT allowed: takesOneArgument ^She jumped and jumped and leapt and even flipped in midair, increasing that distance.^ PopDialogue(); } else if ((mustBeExpression || firstIteration) && TryPopRegex(Regexes.label, LexemeType.LABEL)) { //label must either be: // - the first lexeme on the line, or // - an expression (function argument) } else if (TryEach(nextMustBeExpressionList)) { nextMustBeExpression = true; } else if (TryEach(operators)) { nextMustBeExpression = true; } else if (TryPopRegex(Regexes.comma, LexemeType.COMMA)) { nextMustBeExpression = true; } else if (TryEach(pairs)) { } else if (TryPopRegex(Regexes.word, LexemeType.WORD, out Lexeme lexeme)) { if (!mustBeExpression) { string word = lexeme.text; if (word == "to" || word == "step") { //In For loops, after "to" or "step" an expression is expected. nextMustBeExpression = true; } else if (word == "if" || word == "notif" || word == "for") { //do nothing? } else if (subroutineDatabase.TryGetValue(word, out SubroutineInformation subroutineInformation)) { if (subroutineInformation.hasArguments) { nextMustBeExpression = true; } } else { throw GetLexingException($"Unrecognized keyword or function \"{word}\""); } } } else if (sectionAllowsText && (Peek() == '`')) { //For '`': Pretend single-byte text mode is just normal text mode for now... PopDialogue(); } else if (sectionAllowsText && TryPopRegex(Regexes.hexColor, LexemeType.HEX_COLOR)) { //For '#': If you encounter a color tag at top level, most likely it's for colored text. //Should this enter text mode? break; } else if (sectionAllowsText && (Peek() == '\'')) { PrintLexingWarning($"WARNING: Text-mode possibly entered unintentionally from character '{Peek()}' (ascii: {(int)Peek()})!"); PopDialogue(); } else if (Peek() <= 8) { PrintLexingWarning($"WARNING: Got control character '{Peek()}' (ascii: {(int)Peek()}), which will be ignored!"); lexemes.Add(new Lexeme(LexemeType.UNHANDLED_CONTROL_CHAR, Pop().ToString())); } else { //error throw GetLexingException($"Unexpected character(s)? at top level starting from: [{this.line.Substring(this.pos)}]"); } mustBeExpression = nextMustBeExpression; firstIteration = false; } }