irswat Posted November 6, 2016 Share Posted November 6, 2016 (edited) Had this text file parser almost working. Reworked the logic and optimized some of the code and now I'm stuck on this stupid error. Here is the code: function Main() ;declarations string CurrentDump string CurrentLine string CurrentWord int DumpLineCount=0 int LoopCounter=1 int CurrentWordIndex int WordsOnCurrentLine=0 int CurrentDumpWordIndex=0 ;implement "mic" button debug.trace("Initializing SVE_DumpArray") string[] SVE_DumpArray=new string[90] CurrentDump=Dump_PO() debug.trace("Printing SVE_PO" + CurrentDump) DumpLineCount=GetNumLines(CurrentDump) debug.trace("SVE_PO number of lines: " + DumpLineCount) while (LoopCounter<=DumpLineCount) CurrentLine=GetLine(CurrentDump, LoopCounter, DumpLineCount) debug.trace("Current Line: " + CurrentLine) WordsOnCurrentLine=GetNumWords(CurrentLine) debug.trace("Number of words on current line: " + WordsOnCurrentLine) CurrentWordIndex=1 while (CurrentWordIndex<=WordsOnCurrentLine) ;Get word y on line x ;define CurrentDumpWordIndex CurrentWord=GetWord(CurrentLine, LoopCounter, CurrentWordIndex) ;store word y in array SVE_DumpArray[CurrentDumpWordIndex]=CurrentWord CurrentWordIndex+=1 CurrentDumpWordIndex+=1 endWhile LoopCounter+=1 endWhile endfunction string function Dump_PO() ;shoutout to MinionMaster for helping me understand the syntax and Find the right functions in MiscUtil.psc ;this function is his work string SVE_PO string sDatafileName = "data/SVE_PI.txt" int LineCountParserIndex=0 int EndOfFileFound=0 debug.trace("dumping from " + sDatafileName + "to SVE_PO") if MiscUtil.FileExists(sDatafileName) debug.trace("File found: reading file.") SVE_PO = MiscUtil.ReadFromFile (sDatafileName) EndOfFileFound=Find(SVE_PO, "/delete", LineCountParserIndex) if EndOfFileFound==-1 MiscUtil.WriteToFile(sDatafileName, "/delete", true, false) endif endif return SVE_PO endfunction int function GetNumLines(string SVE_PO) ;Find out how many distinct lines are in the string int LineCountParserIndex=0 int NullCharIndex=0 int LineCount=0 int SVE_PO_End=-1 while (SVE_PO_End==-1) ;delete not found on this line NullCharIndex=Find(SVE_PO, "/n", LineCountParserIndex) ;end of line if NullCharIndex==-1 SVE_PO_End=Find(SVE_PO,"/delete",LineCountParserIndex) ;end of dump file endif LineCount=LineCount+1 LineCountParserIndex=NullCharIndex+3 endWhile return LineCount endFunction string function GetLine(string SVE_PO,int LineNumber,int NumberOfLines) int NullCharIndex=0 int LineStartIndex=0 int EndDumpIndex int LinesToSkip int LinesSkipped string LineReturn if((LineNumber > 1) && (LineNumber < NumberOfLines)) LinesToSkip=LineNumber-1 LinesSkipped=0 while(LinesSkipped<=LinesToSkip) NullCharIndex=Find(SVE_PO, "/n", LineStartIndex) LineStartIndex=NullCharIndex+1 LinesSkipped+=1 endwhile elseif ((LineNumber>1) && (LineNumber==NumberofLines)) LinesToSkip=LineNumber-1 LinesSkipped=0 while(LinesSkipped<=LinesToSkip) EndDumpIndex=Find(SVE_PO, "/Delete", LineStartIndex) LineReturn=Substring(SVE_PO, LineStartIndex, EndDumpIndex) LinesSkipped+=1 endwhile endif NullCharIndex=Find(SVE_PO, "/n", LineStartIndex) if(NullCharIndex > -1) ;"/n" found on line LineReturn=Substring(SVE_PO, LineStartIndex, NullCharIndex) elseif (NullCharIndex==-1) EndDumpIndex=Find(SVE_PO, "/delete", LineStartIndex) if (EndDumpIndex>-1) ;"/delete found" LineReturn=Substring(SVE_PO, LineStartIndex, EndDumpIndex) endif endif return LineReturn endFunction relevant code is in the last function GetLine. "LinesToSkip=LineNumber-1" Edited November 6, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 6, 2016 Author Share Posted November 6, 2016 (edited) FYI the script was about 65% working earlier. It will serve as the foundation of a voice command engine for skyrim, so I am trying to optimize it. Any suggestions are considered. Edited November 6, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 7, 2016 Author Share Posted November 7, 2016 (edited) I comment out these two lines in two places in the function GetLine and the script compiles. It makes no sense to me. no viable alternative at input 'CurrentLineNumber' ;LinesToSkip=CurrentLineNumber-1 ;LinesSkipped=0 Edited November 7, 2016 by irswat Link to comment Share on other sites More sharing options...
irswat Posted November 7, 2016 Author Share Posted November 7, 2016 LinesToSkip=CurrentLineNumber-1I'm using this variable in other parts of the same function with no errors. But subtracting 1 from CurrentLineNumber is causing the compiler to throw an error. Shoutout to whoever helps me figure this one out. Link to comment Share on other sites More sharing options...
mrpwn Posted November 7, 2016 Share Posted November 7, 2016 (edited) After a bit of testing it looks like the lexical analysis part of the compiler has a bug. It seems like it is having trouble telling the difference between a unary minus and a binary minus. It should be generating the following tokens: Identifier: LinesToSkip Binary operator: assignment Identifier: LineNumber Binary operator: subtraction Integer literal: 1 but it is probably generating the following: Identifier: LinesToSkip Binary operator: assignment Identifier: LineNumber Integer literal: -1 Solution: Add a space between the subtraction operator and the integer literal so that this: LinesToSkip=LineNumber-1becomes this: LinesToSkip=LineNumber- 1 I don't think I have noticed that bug before (or maybe I had just forgotten about it already) since I tend to put one space on both sides of an operator. LinesToSkip = LineNumber - 1 Edited November 7, 2016 by mrpwn Link to comment Share on other sites More sharing options...
irswat Posted November 7, 2016 Author Share Posted November 7, 2016 Wow. Thank you so much, and could you tell me how to figured that out?? Link to comment Share on other sites More sharing options...
mrpwn Posted November 7, 2016 Share Posted November 7, 2016 (edited) The explanation that I provided is based on prior experience with writing a linter, which is a piece of software that checks the validity of source code just like a compiler would do in its front end, for Papyrus. I don't have any way of proving that what I said is actually happening since I do not have access to the source code of Bethesda's compiler, but it seems like the most likely scenario to me. The linter is a part of the SublimePapyrus (GitHub repository, Nexus page) package, which I am the primary developer and maintainer of, for Sublime Text 2 and 3. The package has other neat features as well for anyone doing a lot of scripting. As for how I figured it out; at first I could not see what the problem was with your script. I then copied the script into Sublime Text 3 with the SublimePapyrus package installed and let the linter have a look at the script. No errors found, but I have in the past forgotten to implement certain checks, and sometimes I have even managed to avoid bugs, found in Bethesda's compiler. At that point I figured I might need to fix something in my linter. Then I tried compiling the script with the compiler provided by Bethesda and got the same errors that you reported on the lines that you mentioned. Based on a hunch I tried messing with the whitespace between the variable names, operators, and integer literal in the lines that were causing compilation errors. No more errors when compiling with the minus and 1 characters separated by whitespace. It looks like the front end of Bethesda's compiler is based on a parser generated with ANTLR based on the names of certain files found in the same folder as the compiler. I cannot say for sure if the issue you brought up is a bug with ANTLR or just this one particular compiler. I was initially looking into the possibility of using ANTLR myself when I first started writing my own linter as it was my first time writing such a program, but I ended up not going that route. Edit: A bit more detail on my reasoning, if you or anyone else is interested. Lexical analysis deals with what could be considered words (names of variables, names of functions, operators, etc.). This analysis is done by grouping individual characters and generating what are often called tokens. These tokens include additional information such as the row/line number, column number of the first character in the group of characters, type of group (identifier, operator, literal, etc.). Grouping characters can sometimes be tricky as it is context-sensitive and implementation-sensitive. Consider the following two statements: a = 2 - 1 b = -1 + 2Those two lines assign the same value to two different variables once evaluated. However, they produce different token streams and as a result different parse trees. The first line would produce (in a simplified form): Identifier: a Binary operator: assignment Integer literal: 2 Binary operator: subtraction Integeral literal: 1If we did not have any whitespace between - and 1, then we could produce: Identifier: a Binary operator: assignment Integer literal: 2 Unary operator: negation Integeral literal: 1or Identifier: a Binary operator: assignment Integer literal: 2 Integeral literal: -1 but given the context (an integer literal prior to a unary operator or another integer literal) we can figure out that the two integer literals are separated by a binary operator (subtraction). The second line would produce either: Identifier: b Binary operator: assignment Unary operator: negation Integer literal: 1 Binary operator: addition Integer literal: 2or Identifier: b Binary operator: assignment Integer literal: -1 Binary operator: addition Integer literal: 2depending on the implementation. Here are some hastily drawn representations of the parse trees and how they would be evaluated (going from the parse tree at the top to the parse tree at the bottom). Edited November 7, 2016 by mrpwn Link to comment Share on other sites More sharing options...
irswat Posted November 7, 2016 Author Share Posted November 7, 2016 mrpwn, thank you so much for that interesting and in depth reply! Honestly I never would have figured this out on my own.Does SublimePapyrus compile scripts? Link to comment Share on other sites More sharing options...
mrpwn Posted November 7, 2016 Share Posted November 7, 2016 You're welcome. You can compile scripts from within Sublime Text, though it still needs Bethesda's compiler (or a 3rd party compiler) to do so. The settings file of the SublimePapyrus package also allows you to use some of the more advanced features (e.g. multiple import folders), which cannot be used from within CK, that Bethesda's compiler is capable of. You'll still need CK to attach scripts, fill properties, and generate script fragments in quests and dialogue. Outside of those things I think you can do everything else from an external editor such as Sublime Text. Link to comment Share on other sites More sharing options...
irswat Posted November 8, 2016 Author Share Posted November 8, 2016 I'm just using notepad++ currently. Link to comment Share on other sites More sharing options...
Recommended Posts