From b5851a73036c26c8dcf14427b191ba70f72dabd5 Mon Sep 17 00:00:00 2001 From: Alexey Yakovlev Date: Fri, 9 Mar 2012 04:08:01 +0400 Subject: [PATCH 1/5] Enabled assembly shadow copying so that Assembly.LoadFrom(file) doesn't lock the file. --- .hgignore | 11 + .../030.Irony.GrammarExplorer.2010.csproj | 170 ++ ...0.Irony.GrammarExplorer.2010.csproj.vspscc | 10 + Irony.GrammarExplorer/GrammarItemList.cs | 109 ++ Irony.GrammarExplorer/GrammarLoader.cs | 163 ++ .../Highlighter/AboutCodeHighlighter.txt | 1 + .../Highlighter/EditorAdapter.cs | 144 ++ .../Highlighter/EditorViewAdapter.cs | 229 +++ .../Highlighter/RichTextBoxHighlighter.cs | 241 +++ Irony.GrammarExplorer/Program.cs | 63 + .../Properties/AssemblyInfo.cs | 45 + .../Properties/Resources.Designer.cs | 63 + .../Properties/Resources.resx | 117 ++ .../Properties/Settings.Designer.cs | 110 ++ .../Properties/Settings.settings | 27 + Irony.GrammarExplorer/app.config | 33 + .../fmGrammarExplorer.Designer.cs | 1461 +++++++++++++++++ Irony.GrammarExplorer/fmGrammarExplorer.cs | 677 ++++++++ Irony.GrammarExplorer/fmGrammarExplorer.resx | 172 ++ .../fmSelectGrammars.Designer.cs | 127 ++ Irony.GrammarExplorer/fmSelectGrammars.cs | 94 ++ Irony.GrammarExplorer/fmSelectGrammars.resx | 120 ++ .../fmShowException.Designer.cs | 61 + Irony.GrammarExplorer/fmShowException.cs | 33 + Irony.GrammarExplorer/fmShowException.resx | 120 ++ .../015.Irony.Interpreter.2010.csproj | 133 ++ .../Ast/AstContext/InterpreterAstContext.cs | 22 + .../Ast/AstContext/OperatorHandler.cs | 163 ++ Irony.Interpreter/Ast/Base/AstInterfaces.cs | 42 + Irony.Interpreter/Ast/Base/AstNode.cs | 190 +++ Irony.Interpreter/Ast/Base/BasicTypes.cs | 43 + .../Ast/Expressions/BinaryOperationNode.cs | 129 ++ .../Ast/Expressions/ExpressionListNode.cs | 46 + Irony.Interpreter/Ast/Expressions/IfNode.cs | 61 + .../Ast/Expressions/IncDecNode.cs | 68 + .../Ast/Expressions/IndexedAccessNode.cs | 79 + .../Ast/Expressions/MemberAccessNode.cs | 89 + .../Ast/Expressions/UnaryOperationNode.cs | 52 + Irony.Interpreter/Ast/Functions/Closure.cs | 25 + .../Ast/Functions/FunctionCallNode.cs | 130 ++ .../Ast/Functions/FunctionDefNode.cs | 56 + Irony.Interpreter/Ast/Functions/LambdaNode.cs | 96 ++ .../Ast/Functions/ParamListNode.cs | 53 + .../Ast/PrimitiveNodes/IdentifierNode.cs | 56 + .../Ast/PrimitiveNodes/LiteralValueNode.cs | 30 + .../Ast/PrimitiveNodes/StringTemplateNode.cs | 166 ++ .../Ast/SpecialNodes/EmptyStatementNode.cs | 14 + .../Ast/SpecialNodes/NotSupportedNode.cs | 27 + .../Ast/SpecialNodes/NullNode.cs | 23 + .../Ast/Statements/AssignmentNode.cs | 109 ++ .../Ast/Statements/SpecialFormNode.cs | 134 ++ .../Ast/Statements/StatementListNode.cs | 93 ++ Irony.Interpreter/Bindings/Binding.cs | 58 + Irony.Interpreter/Bindings/BindingRequest.cs | 51 + .../Bindings/BindingTargetInfo.cs | 44 + .../Bindings/BuiltInObjectBinding.cs | 79 + .../Bindings/ClrInteropBindings.cs | 168 ++ Irony.Interpreter/Bindings/IBindingSource.cs | 44 + Irony.Interpreter/Bindings/ModuleExport.cs | 36 + Irony.Interpreter/Bindings/SlotBinding.cs | 202 +++ .../Bindings/SpecialFormBinding.cs | 57 + .../Bindings/_about_bindings.txt | 19 + .../Diagnostics/ScriptException.cs | 36 + .../Diagnostics/ScriptStackTrace.cs | 21 + .../InterpretedLanguageGrammar.cs | 64 + .../LanguageRuntime/LanguageRuntime.cs | 82 + .../LanguageRuntime_Binding.cs | 108 ++ .../LanguageRuntime_OpDispatch.cs | 119 ++ .../LanguageRuntime_OpDispatch_Init.cs | 659 ++++++++ .../LanguageRuntime/NoneClass.cs | 40 + .../LanguageRuntime/OperatorImplementation.cs | 202 +++ .../LanguageRuntime/SpecialFormsLibrary.cs | 18 + Irony.Interpreter/Properties/AssemblyInfo.cs | 36 + Irony.Interpreter/Scopes/AppDataMap.cs | 50 + Irony.Interpreter/Scopes/ModuleInfo.cs | 41 + Irony.Interpreter/Scopes/Scope.cs | 71 + Irony.Interpreter/Scopes/ScopeBase.cs | 107 ++ Irony.Interpreter/Scopes/ScopeInfo.cs | 111 ++ .../Scopes/ScopeValuesDictionary.cs | 115 ++ Irony.Interpreter/Scopes/SlotInfo.cs | 47 + Irony.Interpreter/Scopes/_about_storage.txt | 84 + .../SriptApplication/CommandLine.cs | 197 +++ .../SriptApplication/ConsoleAdaptor.cs | 74 + .../SriptApplication/ScriptApp.cs | 231 +++ .../SriptApplication/ScriptThread.cs | 100 ++ Irony.Interpreter/Utilities/Extensions.cs | 20 + Irony.Interpreter/Utilities/Util.cs | 24 + .../_Evaluator/ExpressionEvaluator.cs | 69 + .../_Evaluator/ExpressionEvaluatorGrammar.cs | 182 ++ .../_Evaluator/ExpressionEvaluatorRuntime.cs | 50 + .../_about_Irony_Interpreter.txt | 15 + Irony.Interpreter/irony.snk | Bin 0 -> 596 bytes .../025.Irony.Samples.Console.2010.csproj | 124 ++ ...5.Irony.Samples.Console.2010.csproj.vspscc | 10 + .../025.Irony.Samples.Console.csproj | 73 + Irony.Samples.Console/Program.cs | 43 + .../Properties/AssemblyInfo.cs | 36 + Irony.Samples.Console/app.config | 3 + Irony.Samples/020.Irony.Samples.2010.csproj | 175 ++ .../020.Irony.Samples.2010.csproj.vspscc | 10 + Irony.Samples/CSharp/CSharpGrammar.cs | 881 ++++++++++ Irony.Samples/CSharp/Notes.txt | 46 + Irony.Samples/DataGrammars/JsonGrammar.cs | 39 + .../DataGrammars/SampleCsvGrammar.cs | 37 + .../SearchGrammar.cs | 219 +++ Irony.Samples/GWBasic/GwBasicGrammar.cs | 230 +++ Irony.Samples/Java/JavaGrammar.Static.cs | 770 +++++++++ Irony.Samples/Java/JavaGrammar.Syntax.cs | 800 +++++++++ Irony.Samples/Java/JavaGrammar.cs | 131 ++ Irony.Samples/Java/about_java_grammar.txt | 6 + Irony.Samples/MiniPython/MiniPython.cs | 141 ++ Irony.Samples/Properties/AssemblyInfo.cs | 47 + Irony.Samples/SQL/Sql89-GoldParser.txt | 259 +++ Irony.Samples/SQL/SqlGrammar.cs | 247 +++ .../SampleExpressionEvaluatorGrammar.cs | 28 + Irony.Samples/Scheme/SchemeGrammar.cs | 210 +++ Irony.Samples/SourceSamples/99 bottles.bas | 19 + .../SourceSamples/DataFiles/CarModels.csv | 4 + .../SourceSamples/DataFiles/Sample1.json | 12 + .../SourceSamples/DataFiles/Sample2.json | 22 + .../SourceSamples/DataFiles/Sample3.json | 26 + .../SourceSamples/Java/Sample 1.java | 122 ++ .../SourceSamples/Java/Sample 2.java | 87 + .../SourceSamples/Java/Sample 3.java | 63 + .../SourceSamples/Java/Sample 4.java | 64 + .../SourceSamples/Java/Sample 5.java | 318 ++++ .../SourceSamples/Java/Sample 6.java | 214 +++ .../SourceSamples/Java/about_samples.txt | 1 + .../SourceSamples/MiniPython/PerfTest.py | 119 ++ .../SourceSamples/MiniPython/TestFunCall.py | 5 + .../SourceSamples/SQL/sql_op_test.txt | 4 + .../SourceSamples/SQL/sql_sample1.txt | 7 + .../SourceSamples/SQL/sql_sample2.txt | 21 + .../SourceSamples/SQL/sql_sample3.txt | 25 + .../MemberAccess.txt | 3 + .../SampleExpressionEvaluator/OpSample.txt | 5 + .../SampleExpressionEvaluator/PerfTest.txt | 1207 ++++++++++++++ .../StringSample.txt | 4 + .../TestFormatAndPrint.txt | 3 + .../SampleExpressionEvaluator/TestIif.txt | 3 + .../SampleExpressionEvaluator/UseEnv.txt | 2 + .../SampleExpressionEvaluator/UseMath.txt | 2 + .../_more_samples.txt | 4 + .../Scheme/99 bottles simple.scm | 14 + .../SourceSamples/Scheme/99 bottles.scm | 21 + Irony.Samples/SourceSamples/Scheme/Fib.scm | 6 + Irony.Samples/SourceSamples/Scheme/cadr.scm | 6 + .../SourceSamples/SearchGrammar/Search1.txt | 1 + .../SourceSamples/Wiki/Sample1_codeplex.txt | 70 + .../SourceSamples/Wiki/Sample1_creole.txt | 54 + Irony.Samples/SourceSamples/_about.txt | 4 + .../SourceSamples/c#/TestLessThanOp.cs | 11 + .../SourceSamples/c#/bottles-v11-long.cs | 147 ++ .../SourceSamples/c#/bottles-v11-short.cs | 68 + Irony.Samples/SourceSamples/c#/bottles-v20.cs | 79 + Irony.Samples/SourceSamples/c#/bottles-v30.cs | 44 + .../SourceSamples/c#/escapes_in_IDs.cs | 25 + Irony.Samples/SourceSamples/fib.py | 7 + Irony.Samples/TestGrammars/GrammarEx434.cs | 84 + Irony.Samples/TestGrammars/GrammarEx446.cs | 75 + Irony.Samples/TestGrammars/GrammarExL514.cs | 57 + Irony.Samples/Wiki/WikiCodeplexGrammar.cs | 118 ++ Irony.Samples/Wiki/WikiCreoleGrammar.cs | 133 ++ Irony.Samples/Wiki/WikiHtmlConverter.cs | 254 +++ Irony.Samples/app.config | 19 + .../040.Irony.Tests.VsTest.2010.csproj | 138 ++ Irony.Tests/050.Irony.Tests.NUnit.2010.csproj | 143 ++ .../050.Irony.Tests.NUnit.2010.csproj.vspscc | 10 + Irony.Tests/CommentTerminalTests.cs | 34 + Irony.Tests/DataLiteralsTests.cs | 64 + Irony.Tests/ErrorRecoveryTests.cs | 57 + Irony.Tests/EvaluatorTests.cs | 268 +++ Irony.Tests/FreeTextLiteralTests.cs | 92 ++ Irony.Tests/HeredocTerminalTests.cs | 226 +++ Irony.Tests/IdentifierTerminalTests.cs | 87 + Irony.Tests/IntegrationTests.cs | 105 ++ Irony.Tests/LineContinuationTests.cs | 65 + Irony.Tests/NumberLiteralTests.cs | 403 +++++ Irony.Tests/OperatorTests.cs | 157 ++ Irony.Tests/Properties/AssemblyInfo.cs | 35 + Irony.Tests/RegExLiteralTests.cs | 38 + Irony.Tests/StringLiteralTests.cs | 148 ++ Irony.Tests/TestHelper.cs | 73 + .../ConflictGrammars.cs | 116 ++ .../ConflictResolutionTests.cs | 124 ++ Irony.Tests/_ Tests - read me.txt | 17 + Irony/010.Irony.2010.csproj | 203 +++ Irony/101.IronySilverlight.2010.csproj | 167 ++ Irony/Ast/AstBuilder.cs | 135 ++ Irony/Ast/AstContext.cs | 40 + Irony/Ast/AstExtensions.cs | 29 + Irony/Ast/AstInterfaces.cs | 44 + Irony/Ast/AstNodeConfig.cs | 59 + Irony/MS-PubLicense.Rtf | 177 ++ .../Data/Construction/GrammarDataBuilder.cs | 276 ++++ .../Data/Construction/LanguageDataBuilder.cs | 66 + .../Data/Construction/ParserDataBuilder.cs | 422 +++++ .../ParserDataBuilder_HelperClasses.cs | 289 ++++ .../Data/Construction/ScannerDataBuilder.cs | 130 ++ .../_about_parser_construction.txt | 45 + Irony/Parsing/Data/GrammarData.cs | 41 + Irony/Parsing/Data/LanguageData.cs | 45 + Irony/Parsing/Data/ParserData.cs | 124 ++ Irony/Parsing/Data/ScannerData.cs | 36 + Irony/Parsing/Grammar/BnfExpression.cs | 73 + Irony/Parsing/Grammar/BnfTerm.cs | 237 +++ Irony/Parsing/Grammar/Grammar.cs | 513 ++++++ Irony/Parsing/Grammar/GrammarEnums.cs | 55 + Irony/Parsing/Grammar/GrammarError.cs | 73 + Irony/Parsing/Grammar/GrammarHint.cs | 49 + Irony/Parsing/Grammar/ICanRunSample.cs | 23 + Irony/Parsing/Grammar/LanguageAttribute.cs | 53 + Irony/Parsing/Grammar/NonTerminal.cs | 139 ++ Irony/Parsing/Grammar/TermReportGroups.cs | 50 + Irony/Parsing/Parser/ParseTree.cs | 141 ++ Irony/Parsing/Parser/ParseTreeExtensions.cs | 66 + Irony/Parsing/Parser/Parser.cs | 254 +++ .../ParserActions/AcceptParserAction.cs | 18 + .../ErrorRecoveryParserAction.cs | 90 + .../ParserActions/ReduceParserActions.cs | 162 ++ .../Parser/ParserActions/ShiftParserAction.cs | 31 + .../Parser/ParserActions/_ParserAction.cs | 24 + Irony/Parsing/Parser/ParserDataPrinter.cs | 92 ++ Irony/Parsing/Parser/ParserStack.cs | 47 + Irony/Parsing/Parser/ParserTrace.cs | 51 + Irony/Parsing/Parser/ParsingContext.cs | 291 ++++ Irony/Parsing/Parser/ParsingEventArgs.cs | 44 + .../ConditionalParserAction.cs | 74 + .../CustomActionHintAction.cs | 98 ++ .../ImpliedPrecedenceHint.cs | 54 + .../PrecedenceBasedParserAction.cs | 54 + .../SpecialActionsHints/PrecedenceHint.cs | 53 + .../PreferredActionHint.cs | 57 + .../SpecialActionsHints/TokenPreviewHint.cs | 163 ++ Irony/Parsing/Parser/SyntaxError.cs | 42 + Irony/Parsing/Scanner/Scanner.cs | 322 ++++ Irony/Parsing/Scanner/SourceLocation.cs | 73 + Irony/Parsing/Scanner/SourceStream.cs | 156 ++ Irony/Parsing/Scanner/Token.cs | 99 ++ Irony/Parsing/Scanner/TokenEditorInfo.cs | 108 ++ Irony/Parsing/Scanner/_ISourceStream.cs | 86 + Irony/Parsing/Terminals/CommentTerminal.cs | 119 ++ .../Parsing/Terminals/CompoundTerminalBase.cs | 261 +++ Irony/Parsing/Terminals/ConstantTerminal.cs | 61 + Irony/Parsing/Terminals/CustomTerminal.cs | 46 + Irony/Parsing/Terminals/DataLiteralBase.cs | 58 + Irony/Parsing/Terminals/DsvLiteral.cs | 105 ++ Irony/Parsing/Terminals/FixedLengthLiteral.cs | 39 + Irony/Parsing/Terminals/FreeTextLiteral.cs | 157 ++ Irony/Parsing/Terminals/IdentifierTerminal.cs | 266 +++ .../Terminals/ImpliedSymbolTerminal.cs | 37 + Irony/Parsing/Terminals/KeyTerm.cs | 114 ++ .../Terminals/LineContinuationTerminal.cs | 117 ++ Irony/Parsing/Terminals/NewLineTerminal.cs | 56 + Irony/Parsing/Terminals/NumberLiteral.cs | 507 ++++++ Irony/Parsing/Terminals/QuotedValueLiteral.cs | 33 + Irony/Parsing/Terminals/RegExBasedTerminal.cs | 72 + Irony/Parsing/Terminals/RegExLiteral.cs | 142 ++ Irony/Parsing/Terminals/StringLiteral.cs | 401 +++++ Irony/Parsing/Terminals/TerminalFactory.cs | 176 ++ .../WikiTerminals/WikiBlockTerminal.cs | 44 + .../WikiTerminals/WikiTagTerminal.cs | 36 + .../WikiTerminals/WikiTextTerminal.cs | 54 + .../WikiTerminals/_WikiTerminalBase.cs | 52 + Irony/Parsing/Terminals/_Terminal.cs | 146 ++ .../Parsing/TokenFilters/CodeOutlineFilter.cs | 200 +++ Irony/Parsing/TokenFilters/TokenFilter.cs | 55 + Irony/Properties/AssemblyInfo.cs | 60 + Irony/Resources.Designer.cs | 1071 ++++++++++++ Irony/Resources.resx | 457 ++++++ Irony/SilverlightOnly/Stopwatch.cs | 19 + Irony/Utilities/Extensions.cs | 26 + Irony/Utilities/LogMessage.cs | 52 + Irony/Utilities/StringUtils.cs | 100 ++ Irony/irony.snk | Bin 0 -> 596 bytes Irony_All.2010.sln | 203 +++ Irony_All.2010.vsmdi | 6 + Languages/Refal/Ast/AuxiliaryNode.cs | 68 + Languages/Refal/Ast/Block.cs | 82 + Languages/Refal/Ast/Conditions.cs | 148 ++ Languages/Refal/Ast/DefinedFunction.cs | 75 + Languages/Refal/Ast/Expression.cs | 78 + Languages/Refal/Ast/ExpressionInBraces.cs | 57 + Languages/Refal/Ast/ExpressionVariable.cs | 23 + Languages/Refal/Ast/ExternalFunction.cs | 39 + Languages/Refal/Ast/Function.cs | 36 + Languages/Refal/Ast/FunctionCall.cs | 115 ++ Languages/Refal/Ast/LiteralValueNodeHelper.cs | 46 + Languages/Refal/Ast/Pattern.cs | 85 + Languages/Refal/Ast/Program.cs | 111 ++ Languages/Refal/Ast/ScriptThreadExtensions.cs | 37 + Languages/Refal/Ast/Sentence.cs | 111 ++ Languages/Refal/Ast/SymbolVariable.cs | 23 + Languages/Refal/Ast/TermVariable.cs | 23 + Languages/Refal/Ast/Variable.cs | 106 ++ Languages/Refal/Colorer/colorer.txt | 28 + Languages/Refal/Colorer/refal.hrc | 122 ++ Languages/Refal/IronyAstBase.cd | 41 + Languages/Refal/IronyAstNodes.cd | 73 + Languages/Refal/Properties/AssemblyInfo.cs | 35 + Languages/Refal/Refal.2010.csproj | 99 ++ Languages/Refal/RefalAstNodes.cd | 156 ++ Languages/Refal/RefalGrammar.cs | 133 ++ .../Refal/Runtime/FunctionNamesAttribute.cs | 25 + Languages/Refal/Runtime/LibraryFunction.cs | 86 + Languages/Refal/Runtime/PassiveExpression.cs | 182 ++ Languages/Refal/Runtime/Pattern.cs | 182 ++ Languages/Refal/Runtime/PatternItems.cs | 148 ++ Languages/Refal/Runtime/PatternVariables.cs | 273 +++ .../Refal/Runtime/RecognitionImpossible.cs | 28 + Languages/Refal/Runtime/RefalLibrary.cs | 528 ++++++ .../Refal/Runtime/ReflectionExtensions.cs | 70 + .../UnitTests/Properties/AssemblyInfo.cs | 35 + .../Refal.UnitTests.NUnit.2010.csproj | 145 ++ .../Refal.UnitTests.VsTest.2010.csproj | 142 ++ .../Refal/UnitTests/RefalLibraryTests.cs | 403 +++++ .../UnitTests/RefalPatternMatchingTests.cs | 244 +++ .../Refal/UnitTests/RefalRegressionTests.cs | 188 +++ .../Refal/UnitTests/Sources/99-bottles-v1.ref | 29 + .../Refal/UnitTests/Sources/99-bottles-v1.txt | 396 +++++ .../Refal/UnitTests/Sources/99-bottles-v2.ref | 31 + .../Refal/UnitTests/Sources/99-bottles-v2.txt | 300 ++++ Languages/Refal/UnitTests/Sources/arith.ref | 154 ++ Languages/Refal/UnitTests/Sources/arith.txt | 18 + Languages/Refal/UnitTests/Sources/binary.ref | 31 + Languages/Refal/UnitTests/Sources/binary.txt | 8 + .../Refal/UnitTests/Sources/brainfuck.ref | 114 ++ .../Refal/UnitTests/Sources/brainfuck.txt | 3 + .../Refal/UnitTests/Sources/change-v1.ref | 12 + .../Refal/UnitTests/Sources/change-v2.ref | 11 + Languages/Refal/UnitTests/Sources/change.txt | 1 + .../Refal/UnitTests/Sources/factorial.ref | 11 + .../Refal/UnitTests/Sources/factorial.txt | 1 + Languages/Refal/UnitTests/Sources/hello.ref | 4 + Languages/Refal/UnitTests/Sources/hello.txt | 1 + Languages/Refal/UnitTests/Sources/italian.ref | 29 + Languages/Refal/UnitTests/Sources/italian.txt | 1 + .../Refal/UnitTests/Sources/order-v1.ref | 26 + .../Refal/UnitTests/Sources/order-v2.ref | 24 + Languages/Refal/UnitTests/Sources/order.txt | 3 + .../Refal/UnitTests/Sources/palyndrome.ref | 15 + .../Refal/UnitTests/Sources/palyndrome.txt | 3 + Languages/Refal/UnitTests/Sources/pretty.ref | 29 + Languages/Refal/UnitTests/Sources/pretty.txt | 13 + .../Refal/UnitTests/Sources/quine-plain.ref | 4 + .../Refal/UnitTests/Sources/quine-simple.ref | 48 + .../UnitTests/Sources/quine-xplained.ref | 28 + .../Refal/UnitTests/Sources/xtras-bigint.ref | 9 + .../Refal/UnitTests/Sources/xtras-bigint.txt | 1 + .../UnitTests/Sources/xtras-factorial.ref | 11 + .../UnitTests/Sources/xtras-factorial.txt | 1 + License.txt | 16 + ReadMe.txt | 43 + 353 files changed, 38638 insertions(+) create mode 100644 .hgignore create mode 100644 Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj create mode 100644 Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj.vspscc create mode 100644 Irony.GrammarExplorer/GrammarItemList.cs create mode 100644 Irony.GrammarExplorer/GrammarLoader.cs create mode 100644 Irony.GrammarExplorer/Highlighter/AboutCodeHighlighter.txt create mode 100644 Irony.GrammarExplorer/Highlighter/EditorAdapter.cs create mode 100644 Irony.GrammarExplorer/Highlighter/EditorViewAdapter.cs create mode 100644 Irony.GrammarExplorer/Highlighter/RichTextBoxHighlighter.cs create mode 100644 Irony.GrammarExplorer/Program.cs create mode 100644 Irony.GrammarExplorer/Properties/AssemblyInfo.cs create mode 100644 Irony.GrammarExplorer/Properties/Resources.Designer.cs create mode 100644 Irony.GrammarExplorer/Properties/Resources.resx create mode 100644 Irony.GrammarExplorer/Properties/Settings.Designer.cs create mode 100644 Irony.GrammarExplorer/Properties/Settings.settings create mode 100644 Irony.GrammarExplorer/app.config create mode 100644 Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs create mode 100644 Irony.GrammarExplorer/fmGrammarExplorer.cs create mode 100644 Irony.GrammarExplorer/fmGrammarExplorer.resx create mode 100644 Irony.GrammarExplorer/fmSelectGrammars.Designer.cs create mode 100644 Irony.GrammarExplorer/fmSelectGrammars.cs create mode 100644 Irony.GrammarExplorer/fmSelectGrammars.resx create mode 100644 Irony.GrammarExplorer/fmShowException.Designer.cs create mode 100644 Irony.GrammarExplorer/fmShowException.cs create mode 100644 Irony.GrammarExplorer/fmShowException.resx create mode 100644 Irony.Interpreter/015.Irony.Interpreter.2010.csproj create mode 100644 Irony.Interpreter/Ast/AstContext/InterpreterAstContext.cs create mode 100644 Irony.Interpreter/Ast/AstContext/OperatorHandler.cs create mode 100644 Irony.Interpreter/Ast/Base/AstInterfaces.cs create mode 100644 Irony.Interpreter/Ast/Base/AstNode.cs create mode 100644 Irony.Interpreter/Ast/Base/BasicTypes.cs create mode 100644 Irony.Interpreter/Ast/Expressions/BinaryOperationNode.cs create mode 100644 Irony.Interpreter/Ast/Expressions/ExpressionListNode.cs create mode 100644 Irony.Interpreter/Ast/Expressions/IfNode.cs create mode 100644 Irony.Interpreter/Ast/Expressions/IncDecNode.cs create mode 100644 Irony.Interpreter/Ast/Expressions/IndexedAccessNode.cs create mode 100644 Irony.Interpreter/Ast/Expressions/MemberAccessNode.cs create mode 100644 Irony.Interpreter/Ast/Expressions/UnaryOperationNode.cs create mode 100644 Irony.Interpreter/Ast/Functions/Closure.cs create mode 100644 Irony.Interpreter/Ast/Functions/FunctionCallNode.cs create mode 100644 Irony.Interpreter/Ast/Functions/FunctionDefNode.cs create mode 100644 Irony.Interpreter/Ast/Functions/LambdaNode.cs create mode 100644 Irony.Interpreter/Ast/Functions/ParamListNode.cs create mode 100644 Irony.Interpreter/Ast/PrimitiveNodes/IdentifierNode.cs create mode 100644 Irony.Interpreter/Ast/PrimitiveNodes/LiteralValueNode.cs create mode 100644 Irony.Interpreter/Ast/PrimitiveNodes/StringTemplateNode.cs create mode 100644 Irony.Interpreter/Ast/SpecialNodes/EmptyStatementNode.cs create mode 100644 Irony.Interpreter/Ast/SpecialNodes/NotSupportedNode.cs create mode 100644 Irony.Interpreter/Ast/SpecialNodes/NullNode.cs create mode 100644 Irony.Interpreter/Ast/Statements/AssignmentNode.cs create mode 100644 Irony.Interpreter/Ast/Statements/SpecialFormNode.cs create mode 100644 Irony.Interpreter/Ast/Statements/StatementListNode.cs create mode 100644 Irony.Interpreter/Bindings/Binding.cs create mode 100644 Irony.Interpreter/Bindings/BindingRequest.cs create mode 100644 Irony.Interpreter/Bindings/BindingTargetInfo.cs create mode 100644 Irony.Interpreter/Bindings/BuiltInObjectBinding.cs create mode 100644 Irony.Interpreter/Bindings/ClrInteropBindings.cs create mode 100644 Irony.Interpreter/Bindings/IBindingSource.cs create mode 100644 Irony.Interpreter/Bindings/ModuleExport.cs create mode 100644 Irony.Interpreter/Bindings/SlotBinding.cs create mode 100644 Irony.Interpreter/Bindings/SpecialFormBinding.cs create mode 100644 Irony.Interpreter/Bindings/_about_bindings.txt create mode 100644 Irony.Interpreter/Diagnostics/ScriptException.cs create mode 100644 Irony.Interpreter/Diagnostics/ScriptStackTrace.cs create mode 100644 Irony.Interpreter/InterpretedLanguageGrammar.cs create mode 100644 Irony.Interpreter/LanguageRuntime/LanguageRuntime.cs create mode 100644 Irony.Interpreter/LanguageRuntime/LanguageRuntime_Binding.cs create mode 100644 Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch.cs create mode 100644 Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch_Init.cs create mode 100644 Irony.Interpreter/LanguageRuntime/NoneClass.cs create mode 100644 Irony.Interpreter/LanguageRuntime/OperatorImplementation.cs create mode 100644 Irony.Interpreter/LanguageRuntime/SpecialFormsLibrary.cs create mode 100644 Irony.Interpreter/Properties/AssemblyInfo.cs create mode 100644 Irony.Interpreter/Scopes/AppDataMap.cs create mode 100644 Irony.Interpreter/Scopes/ModuleInfo.cs create mode 100644 Irony.Interpreter/Scopes/Scope.cs create mode 100644 Irony.Interpreter/Scopes/ScopeBase.cs create mode 100644 Irony.Interpreter/Scopes/ScopeInfo.cs create mode 100644 Irony.Interpreter/Scopes/ScopeValuesDictionary.cs create mode 100644 Irony.Interpreter/Scopes/SlotInfo.cs create mode 100644 Irony.Interpreter/Scopes/_about_storage.txt create mode 100644 Irony.Interpreter/SriptApplication/CommandLine.cs create mode 100644 Irony.Interpreter/SriptApplication/ConsoleAdaptor.cs create mode 100644 Irony.Interpreter/SriptApplication/ScriptApp.cs create mode 100644 Irony.Interpreter/SriptApplication/ScriptThread.cs create mode 100644 Irony.Interpreter/Utilities/Extensions.cs create mode 100644 Irony.Interpreter/Utilities/Util.cs create mode 100644 Irony.Interpreter/_Evaluator/ExpressionEvaluator.cs create mode 100644 Irony.Interpreter/_Evaluator/ExpressionEvaluatorGrammar.cs create mode 100644 Irony.Interpreter/_Evaluator/ExpressionEvaluatorRuntime.cs create mode 100644 Irony.Interpreter/_about_Irony_Interpreter.txt create mode 100644 Irony.Interpreter/irony.snk create mode 100644 Irony.Samples.Console/025.Irony.Samples.Console.2010.csproj create mode 100644 Irony.Samples.Console/025.Irony.Samples.Console.2010.csproj.vspscc create mode 100644 Irony.Samples.Console/025.Irony.Samples.Console.csproj create mode 100644 Irony.Samples.Console/Program.cs create mode 100644 Irony.Samples.Console/Properties/AssemblyInfo.cs create mode 100644 Irony.Samples.Console/app.config create mode 100644 Irony.Samples/020.Irony.Samples.2010.csproj create mode 100644 Irony.Samples/020.Irony.Samples.2010.csproj.vspscc create mode 100644 Irony.Samples/CSharp/CSharpGrammar.cs create mode 100644 Irony.Samples/CSharp/Notes.txt create mode 100644 Irony.Samples/DataGrammars/JsonGrammar.cs create mode 100644 Irony.Samples/DataGrammars/SampleCsvGrammar.cs create mode 100644 Irony.Samples/FullTextSearchQueryConverter/SearchGrammar.cs create mode 100644 Irony.Samples/GWBasic/GwBasicGrammar.cs create mode 100644 Irony.Samples/Java/JavaGrammar.Static.cs create mode 100644 Irony.Samples/Java/JavaGrammar.Syntax.cs create mode 100644 Irony.Samples/Java/JavaGrammar.cs create mode 100644 Irony.Samples/Java/about_java_grammar.txt create mode 100644 Irony.Samples/MiniPython/MiniPython.cs create mode 100644 Irony.Samples/Properties/AssemblyInfo.cs create mode 100644 Irony.Samples/SQL/Sql89-GoldParser.txt create mode 100644 Irony.Samples/SQL/SqlGrammar.cs create mode 100644 Irony.Samples/SampleExpressionEvaluator/SampleExpressionEvaluatorGrammar.cs create mode 100644 Irony.Samples/Scheme/SchemeGrammar.cs create mode 100644 Irony.Samples/SourceSamples/99 bottles.bas create mode 100644 Irony.Samples/SourceSamples/DataFiles/CarModels.csv create mode 100644 Irony.Samples/SourceSamples/DataFiles/Sample1.json create mode 100644 Irony.Samples/SourceSamples/DataFiles/Sample2.json create mode 100644 Irony.Samples/SourceSamples/DataFiles/Sample3.json create mode 100644 Irony.Samples/SourceSamples/Java/Sample 1.java create mode 100644 Irony.Samples/SourceSamples/Java/Sample 2.java create mode 100644 Irony.Samples/SourceSamples/Java/Sample 3.java create mode 100644 Irony.Samples/SourceSamples/Java/Sample 4.java create mode 100644 Irony.Samples/SourceSamples/Java/Sample 5.java create mode 100644 Irony.Samples/SourceSamples/Java/Sample 6.java create mode 100644 Irony.Samples/SourceSamples/Java/about_samples.txt create mode 100644 Irony.Samples/SourceSamples/MiniPython/PerfTest.py create mode 100644 Irony.Samples/SourceSamples/MiniPython/TestFunCall.py create mode 100644 Irony.Samples/SourceSamples/SQL/sql_op_test.txt create mode 100644 Irony.Samples/SourceSamples/SQL/sql_sample1.txt create mode 100644 Irony.Samples/SourceSamples/SQL/sql_sample2.txt create mode 100644 Irony.Samples/SourceSamples/SQL/sql_sample3.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/MemberAccess.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/OpSample.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/PerfTest.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/StringSample.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestFormatAndPrint.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestIif.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseEnv.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseMath.txt create mode 100644 Irony.Samples/SourceSamples/SampleExpressionEvaluator/_more_samples.txt create mode 100644 Irony.Samples/SourceSamples/Scheme/99 bottles simple.scm create mode 100644 Irony.Samples/SourceSamples/Scheme/99 bottles.scm create mode 100644 Irony.Samples/SourceSamples/Scheme/Fib.scm create mode 100644 Irony.Samples/SourceSamples/Scheme/cadr.scm create mode 100644 Irony.Samples/SourceSamples/SearchGrammar/Search1.txt create mode 100644 Irony.Samples/SourceSamples/Wiki/Sample1_codeplex.txt create mode 100644 Irony.Samples/SourceSamples/Wiki/Sample1_creole.txt create mode 100644 Irony.Samples/SourceSamples/_about.txt create mode 100644 Irony.Samples/SourceSamples/c#/TestLessThanOp.cs create mode 100644 Irony.Samples/SourceSamples/c#/bottles-v11-long.cs create mode 100644 Irony.Samples/SourceSamples/c#/bottles-v11-short.cs create mode 100644 Irony.Samples/SourceSamples/c#/bottles-v20.cs create mode 100644 Irony.Samples/SourceSamples/c#/bottles-v30.cs create mode 100644 Irony.Samples/SourceSamples/c#/escapes_in_IDs.cs create mode 100644 Irony.Samples/SourceSamples/fib.py create mode 100644 Irony.Samples/TestGrammars/GrammarEx434.cs create mode 100644 Irony.Samples/TestGrammars/GrammarEx446.cs create mode 100644 Irony.Samples/TestGrammars/GrammarExL514.cs create mode 100644 Irony.Samples/Wiki/WikiCodeplexGrammar.cs create mode 100644 Irony.Samples/Wiki/WikiCreoleGrammar.cs create mode 100644 Irony.Samples/Wiki/WikiHtmlConverter.cs create mode 100644 Irony.Samples/app.config create mode 100644 Irony.Tests/040.Irony.Tests.VsTest.2010.csproj create mode 100644 Irony.Tests/050.Irony.Tests.NUnit.2010.csproj create mode 100644 Irony.Tests/050.Irony.Tests.NUnit.2010.csproj.vspscc create mode 100644 Irony.Tests/CommentTerminalTests.cs create mode 100644 Irony.Tests/DataLiteralsTests.cs create mode 100644 Irony.Tests/ErrorRecoveryTests.cs create mode 100644 Irony.Tests/EvaluatorTests.cs create mode 100644 Irony.Tests/FreeTextLiteralTests.cs create mode 100644 Irony.Tests/HeredocTerminalTests.cs create mode 100644 Irony.Tests/IdentifierTerminalTests.cs create mode 100644 Irony.Tests/IntegrationTests.cs create mode 100644 Irony.Tests/LineContinuationTests.cs create mode 100644 Irony.Tests/NumberLiteralTests.cs create mode 100644 Irony.Tests/OperatorTests.cs create mode 100644 Irony.Tests/Properties/AssemblyInfo.cs create mode 100644 Irony.Tests/RegExLiteralTests.cs create mode 100644 Irony.Tests/StringLiteralTests.cs create mode 100644 Irony.Tests/TestHelper.cs create mode 100644 Irony.Tests/TokenPreviewResolution/ConflictGrammars.cs create mode 100644 Irony.Tests/TokenPreviewResolution/ConflictResolutionTests.cs create mode 100644 Irony.Tests/_ Tests - read me.txt create mode 100644 Irony/010.Irony.2010.csproj create mode 100644 Irony/101.IronySilverlight.2010.csproj create mode 100644 Irony/Ast/AstBuilder.cs create mode 100644 Irony/Ast/AstContext.cs create mode 100644 Irony/Ast/AstExtensions.cs create mode 100644 Irony/Ast/AstInterfaces.cs create mode 100644 Irony/Ast/AstNodeConfig.cs create mode 100644 Irony/MS-PubLicense.Rtf create mode 100644 Irony/Parsing/Data/Construction/GrammarDataBuilder.cs create mode 100644 Irony/Parsing/Data/Construction/LanguageDataBuilder.cs create mode 100644 Irony/Parsing/Data/Construction/ParserDataBuilder.cs create mode 100644 Irony/Parsing/Data/Construction/ParserDataBuilder_HelperClasses.cs create mode 100644 Irony/Parsing/Data/Construction/ScannerDataBuilder.cs create mode 100644 Irony/Parsing/Data/Construction/_about_parser_construction.txt create mode 100644 Irony/Parsing/Data/GrammarData.cs create mode 100644 Irony/Parsing/Data/LanguageData.cs create mode 100644 Irony/Parsing/Data/ParserData.cs create mode 100644 Irony/Parsing/Data/ScannerData.cs create mode 100644 Irony/Parsing/Grammar/BnfExpression.cs create mode 100644 Irony/Parsing/Grammar/BnfTerm.cs create mode 100644 Irony/Parsing/Grammar/Grammar.cs create mode 100644 Irony/Parsing/Grammar/GrammarEnums.cs create mode 100644 Irony/Parsing/Grammar/GrammarError.cs create mode 100644 Irony/Parsing/Grammar/GrammarHint.cs create mode 100644 Irony/Parsing/Grammar/ICanRunSample.cs create mode 100644 Irony/Parsing/Grammar/LanguageAttribute.cs create mode 100644 Irony/Parsing/Grammar/NonTerminal.cs create mode 100644 Irony/Parsing/Grammar/TermReportGroups.cs create mode 100644 Irony/Parsing/Parser/ParseTree.cs create mode 100644 Irony/Parsing/Parser/ParseTreeExtensions.cs create mode 100644 Irony/Parsing/Parser/Parser.cs create mode 100644 Irony/Parsing/Parser/ParserActions/AcceptParserAction.cs create mode 100644 Irony/Parsing/Parser/ParserActions/ErrorRecoveryParserAction.cs create mode 100644 Irony/Parsing/Parser/ParserActions/ReduceParserActions.cs create mode 100644 Irony/Parsing/Parser/ParserActions/ShiftParserAction.cs create mode 100644 Irony/Parsing/Parser/ParserActions/_ParserAction.cs create mode 100644 Irony/Parsing/Parser/ParserDataPrinter.cs create mode 100644 Irony/Parsing/Parser/ParserStack.cs create mode 100644 Irony/Parsing/Parser/ParserTrace.cs create mode 100644 Irony/Parsing/Parser/ParsingContext.cs create mode 100644 Irony/Parsing/Parser/ParsingEventArgs.cs create mode 100644 Irony/Parsing/Parser/SpecialActionsHints/ConditionalParserAction.cs create mode 100644 Irony/Parsing/Parser/SpecialActionsHints/CustomActionHintAction.cs create mode 100644 Irony/Parsing/Parser/SpecialActionsHints/ImpliedPrecedenceHint.cs create mode 100644 Irony/Parsing/Parser/SpecialActionsHints/PrecedenceBasedParserAction.cs create mode 100644 Irony/Parsing/Parser/SpecialActionsHints/PrecedenceHint.cs create mode 100644 Irony/Parsing/Parser/SpecialActionsHints/PreferredActionHint.cs create mode 100644 Irony/Parsing/Parser/SpecialActionsHints/TokenPreviewHint.cs create mode 100644 Irony/Parsing/Parser/SyntaxError.cs create mode 100644 Irony/Parsing/Scanner/Scanner.cs create mode 100644 Irony/Parsing/Scanner/SourceLocation.cs create mode 100644 Irony/Parsing/Scanner/SourceStream.cs create mode 100644 Irony/Parsing/Scanner/Token.cs create mode 100644 Irony/Parsing/Scanner/TokenEditorInfo.cs create mode 100644 Irony/Parsing/Scanner/_ISourceStream.cs create mode 100644 Irony/Parsing/Terminals/CommentTerminal.cs create mode 100644 Irony/Parsing/Terminals/CompoundTerminalBase.cs create mode 100644 Irony/Parsing/Terminals/ConstantTerminal.cs create mode 100644 Irony/Parsing/Terminals/CustomTerminal.cs create mode 100644 Irony/Parsing/Terminals/DataLiteralBase.cs create mode 100644 Irony/Parsing/Terminals/DsvLiteral.cs create mode 100644 Irony/Parsing/Terminals/FixedLengthLiteral.cs create mode 100644 Irony/Parsing/Terminals/FreeTextLiteral.cs create mode 100644 Irony/Parsing/Terminals/IdentifierTerminal.cs create mode 100644 Irony/Parsing/Terminals/ImpliedSymbolTerminal.cs create mode 100644 Irony/Parsing/Terminals/KeyTerm.cs create mode 100644 Irony/Parsing/Terminals/LineContinuationTerminal.cs create mode 100644 Irony/Parsing/Terminals/NewLineTerminal.cs create mode 100644 Irony/Parsing/Terminals/NumberLiteral.cs create mode 100644 Irony/Parsing/Terminals/QuotedValueLiteral.cs create mode 100644 Irony/Parsing/Terminals/RegExBasedTerminal.cs create mode 100644 Irony/Parsing/Terminals/RegExLiteral.cs create mode 100644 Irony/Parsing/Terminals/StringLiteral.cs create mode 100644 Irony/Parsing/Terminals/TerminalFactory.cs create mode 100644 Irony/Parsing/Terminals/WikiTerminals/WikiBlockTerminal.cs create mode 100644 Irony/Parsing/Terminals/WikiTerminals/WikiTagTerminal.cs create mode 100644 Irony/Parsing/Terminals/WikiTerminals/WikiTextTerminal.cs create mode 100644 Irony/Parsing/Terminals/WikiTerminals/_WikiTerminalBase.cs create mode 100644 Irony/Parsing/Terminals/_Terminal.cs create mode 100644 Irony/Parsing/TokenFilters/CodeOutlineFilter.cs create mode 100644 Irony/Parsing/TokenFilters/TokenFilter.cs create mode 100644 Irony/Properties/AssemblyInfo.cs create mode 100644 Irony/Resources.Designer.cs create mode 100644 Irony/Resources.resx create mode 100644 Irony/SilverlightOnly/Stopwatch.cs create mode 100644 Irony/Utilities/Extensions.cs create mode 100644 Irony/Utilities/LogMessage.cs create mode 100644 Irony/Utilities/StringUtils.cs create mode 100644 Irony/irony.snk create mode 100644 Irony_All.2010.sln create mode 100644 Irony_All.2010.vsmdi create mode 100644 Languages/Refal/Ast/AuxiliaryNode.cs create mode 100644 Languages/Refal/Ast/Block.cs create mode 100644 Languages/Refal/Ast/Conditions.cs create mode 100644 Languages/Refal/Ast/DefinedFunction.cs create mode 100644 Languages/Refal/Ast/Expression.cs create mode 100644 Languages/Refal/Ast/ExpressionInBraces.cs create mode 100644 Languages/Refal/Ast/ExpressionVariable.cs create mode 100644 Languages/Refal/Ast/ExternalFunction.cs create mode 100644 Languages/Refal/Ast/Function.cs create mode 100644 Languages/Refal/Ast/FunctionCall.cs create mode 100644 Languages/Refal/Ast/LiteralValueNodeHelper.cs create mode 100644 Languages/Refal/Ast/Pattern.cs create mode 100644 Languages/Refal/Ast/Program.cs create mode 100644 Languages/Refal/Ast/ScriptThreadExtensions.cs create mode 100644 Languages/Refal/Ast/Sentence.cs create mode 100644 Languages/Refal/Ast/SymbolVariable.cs create mode 100644 Languages/Refal/Ast/TermVariable.cs create mode 100644 Languages/Refal/Ast/Variable.cs create mode 100644 Languages/Refal/Colorer/colorer.txt create mode 100644 Languages/Refal/Colorer/refal.hrc create mode 100644 Languages/Refal/IronyAstBase.cd create mode 100644 Languages/Refal/IronyAstNodes.cd create mode 100644 Languages/Refal/Properties/AssemblyInfo.cs create mode 100644 Languages/Refal/Refal.2010.csproj create mode 100644 Languages/Refal/RefalAstNodes.cd create mode 100644 Languages/Refal/RefalGrammar.cs create mode 100644 Languages/Refal/Runtime/FunctionNamesAttribute.cs create mode 100644 Languages/Refal/Runtime/LibraryFunction.cs create mode 100644 Languages/Refal/Runtime/PassiveExpression.cs create mode 100644 Languages/Refal/Runtime/Pattern.cs create mode 100644 Languages/Refal/Runtime/PatternItems.cs create mode 100644 Languages/Refal/Runtime/PatternVariables.cs create mode 100644 Languages/Refal/Runtime/RecognitionImpossible.cs create mode 100644 Languages/Refal/Runtime/RefalLibrary.cs create mode 100644 Languages/Refal/Runtime/ReflectionExtensions.cs create mode 100644 Languages/Refal/UnitTests/Properties/AssemblyInfo.cs create mode 100644 Languages/Refal/UnitTests/Refal.UnitTests.NUnit.2010.csproj create mode 100644 Languages/Refal/UnitTests/Refal.UnitTests.VsTest.2010.csproj create mode 100644 Languages/Refal/UnitTests/RefalLibraryTests.cs create mode 100644 Languages/Refal/UnitTests/RefalPatternMatchingTests.cs create mode 100644 Languages/Refal/UnitTests/RefalRegressionTests.cs create mode 100644 Languages/Refal/UnitTests/Sources/99-bottles-v1.ref create mode 100644 Languages/Refal/UnitTests/Sources/99-bottles-v1.txt create mode 100644 Languages/Refal/UnitTests/Sources/99-bottles-v2.ref create mode 100644 Languages/Refal/UnitTests/Sources/99-bottles-v2.txt create mode 100644 Languages/Refal/UnitTests/Sources/arith.ref create mode 100644 Languages/Refal/UnitTests/Sources/arith.txt create mode 100644 Languages/Refal/UnitTests/Sources/binary.ref create mode 100644 Languages/Refal/UnitTests/Sources/binary.txt create mode 100644 Languages/Refal/UnitTests/Sources/brainfuck.ref create mode 100644 Languages/Refal/UnitTests/Sources/brainfuck.txt create mode 100644 Languages/Refal/UnitTests/Sources/change-v1.ref create mode 100644 Languages/Refal/UnitTests/Sources/change-v2.ref create mode 100644 Languages/Refal/UnitTests/Sources/change.txt create mode 100644 Languages/Refal/UnitTests/Sources/factorial.ref create mode 100644 Languages/Refal/UnitTests/Sources/factorial.txt create mode 100644 Languages/Refal/UnitTests/Sources/hello.ref create mode 100644 Languages/Refal/UnitTests/Sources/hello.txt create mode 100644 Languages/Refal/UnitTests/Sources/italian.ref create mode 100644 Languages/Refal/UnitTests/Sources/italian.txt create mode 100644 Languages/Refal/UnitTests/Sources/order-v1.ref create mode 100644 Languages/Refal/UnitTests/Sources/order-v2.ref create mode 100644 Languages/Refal/UnitTests/Sources/order.txt create mode 100644 Languages/Refal/UnitTests/Sources/palyndrome.ref create mode 100644 Languages/Refal/UnitTests/Sources/palyndrome.txt create mode 100644 Languages/Refal/UnitTests/Sources/pretty.ref create mode 100644 Languages/Refal/UnitTests/Sources/pretty.txt create mode 100644 Languages/Refal/UnitTests/Sources/quine-plain.ref create mode 100644 Languages/Refal/UnitTests/Sources/quine-simple.ref create mode 100644 Languages/Refal/UnitTests/Sources/quine-xplained.ref create mode 100644 Languages/Refal/UnitTests/Sources/xtras-bigint.ref create mode 100644 Languages/Refal/UnitTests/Sources/xtras-bigint.txt create mode 100644 Languages/Refal/UnitTests/Sources/xtras-factorial.ref create mode 100644 Languages/Refal/UnitTests/Sources/xtras-factorial.txt create mode 100644 License.txt create mode 100644 ReadMe.txt diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..3915fea --- /dev/null +++ b/.hgignore @@ -0,0 +1,11 @@ +syntax: glob +*.suo +*.user +*.cache +*.Cache +*.FileListAbsolute.txt +*.gpState +bin +obj +*.lnk +TestResults/* diff --git a/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj b/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj new file mode 100644 index 0000000..b2d8ecd --- /dev/null +++ b/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj @@ -0,0 +1,170 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {AE1C04A8-B03D-4144-98E9-BF66C50265E7} + WinExe + Properties + Irony.GrammarExplorer + Irony.GrammarExplorer + + + 3.5 + + + v4.0 + + + + + + + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + + + + + + + + Form + + + fmGrammarExplorer.cs + + + Form + + + fmSelectGrammars.cs + + + + + + + + + fmGrammarExplorer.cs + Designer + + + fmSelectGrammars.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + Designer + fmShowException.cs + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + Form + + + fmShowException.cs + + + + + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj.vspscc b/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/Irony.GrammarExplorer/030.Irony.GrammarExplorer.2010.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Irony.GrammarExplorer/GrammarItemList.cs b/Irony.GrammarExplorer/GrammarItemList.cs new file mode 100644 index 0000000..172d432 --- /dev/null +++ b/Irony.GrammarExplorer/GrammarItemList.cs @@ -0,0 +1,109 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.Windows.Forms; +using System.Reflection; +using Irony.Parsing; +using System.IO; +using System.Threading; + +namespace Irony.GrammarExplorer { + + //Helper classes for supporting showing grammar list in top combo, saving list on exit and loading on start + public class GrammarItem { + public readonly string Caption; + public readonly string LongCaption; + public readonly string Location; //location of assembly containing the grammar + public readonly string TypeName; //full type name + internal bool _loading; + public GrammarItem(string caption, string location, string typeName) { + Caption = caption; + Location = location; + TypeName = typeName; + } + public GrammarItem(Type grammarClass, string assemblyLocation) { + _loading = true; + Location = assemblyLocation; + TypeName = grammarClass.FullName; + //Get language name from Language attribute + Caption = grammarClass.Name; //default caption + LongCaption = Caption; + var langAttr = LanguageAttribute.GetValue(grammarClass); + if (langAttr != null) { + Caption = langAttr.LanguageName; + if (!string.IsNullOrEmpty(langAttr.Version)) + Caption += ", version " + langAttr.Version; + LongCaption = Caption; + if (!string.IsNullOrEmpty(langAttr.Description)) + LongCaption += ": " + langAttr.Description; + } + } + public GrammarItem(XmlElement element) { + Caption = element.GetAttribute("Caption"); + Location = element.GetAttribute("Location"); + TypeName = element.GetAttribute("TypeName"); + } + public void Save(XmlElement toElement) { + toElement.SetAttribute("Caption", Caption); + toElement.SetAttribute("Location", Location); + toElement.SetAttribute("TypeName", TypeName); + } + public override string ToString() { + return _loading ? LongCaption : Caption; + } + + }//class + + public class GrammarItemList : List { + public static GrammarItemList FromXml(string xml) { + GrammarItemList list = new GrammarItemList(); + XmlDocument xdoc = new XmlDocument(); + xdoc.LoadXml(xml); + XmlNodeList xlist = xdoc.SelectNodes("//Grammar"); + foreach (XmlElement xitem in xlist) { + GrammarItem item = new GrammarItem(xitem); + list.Add(item); + } + return list; + } + public static GrammarItemList FromCombo(ComboBox combo) { + GrammarItemList list = new GrammarItemList(); + foreach (GrammarItem item in combo.Items) + list.Add(item); + return list; + } + + public string ToXml() { + XmlDocument xdoc = new XmlDocument(); + XmlElement xlist = xdoc.CreateElement("Grammars"); + xdoc.AppendChild(xlist); + foreach (GrammarItem item in this) { + XmlElement xitem = xdoc.CreateElement("Grammar"); + xlist.AppendChild(xitem); + item.Save(xitem); + } //foreach + return xdoc.OuterXml; + }//method + + public void ShowIn(ComboBox combo) { + combo.Items.Clear(); + foreach (GrammarItem item in this) + combo.Items.Add(item); + } + + }//class +} diff --git a/Irony.GrammarExplorer/GrammarLoader.cs b/Irony.GrammarExplorer/GrammarLoader.cs new file mode 100644 index 0000000..0b1b079 --- /dev/null +++ b/Irony.GrammarExplorer/GrammarLoader.cs @@ -0,0 +1,163 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +// This file and all functionality of dynamic assembly reloading was contributed by Alexey Yakovlev (yallie) +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using Irony.Parsing; +using System.IO; +using System.Threading; + +namespace Irony.GrammarExplorer { + /// + /// Maintains grammar assemblies, reloads updated files automatically. + /// + class GrammarLoader { + private TimeSpan _autoRefreshDelay = TimeSpan.FromMilliseconds(500); + private Dictionary _cachedGrammarAssemblies = new Dictionary(); + private static HashSet _probingPaths = new HashSet(); + private static Dictionary _loadedAssemblies = new Dictionary(); + private static bool _enableAssemblyResolution = false; + + static GrammarLoader() { + AppDomain.CurrentDomain.AssemblyLoad += (s, args) => _loadedAssemblies[args.LoadedAssembly.FullName] = args.LoadedAssembly; + AppDomain.CurrentDomain.AssemblyResolve += (s, args) => _enableAssemblyResolution ? FindAssembly(args.Name) : null; + } + + static Assembly FindAssembly(string assemblyName) { + if (_loadedAssemblies.ContainsKey(assemblyName)) { + return _loadedAssemblies[assemblyName]; + } + // use probing paths to look for assemblies + var fileName = assemblyName.Split(',').First() + ".dll"; + foreach (var path in _probingPaths) { + var fullName = Path.Combine(path, fileName); + if (File.Exists(fullName)) { + try { + return _loadedAssemblies[assemblyName] = Assembly.LoadFrom(fullName); + } + catch { + // the file seems to be bad, let's try to find another one + } + } + } + // assembly not found, don't search for it again + return _loadedAssemblies[assemblyName] = null; + } + + class CachedAssembly { + public long FileSize; + public DateTime LastWriteTime; + public FileSystemWatcher Watcher; + public Assembly Assembly; + } + + public event EventHandler AssemblyUpdated; + + public GrammarItem SelectedGrammar { get; set; } + + public Grammar CreateGrammar() { + if (SelectedGrammar == null) + return null; + + // resolve dependencies while loading and creating grammars + _enableAssemblyResolution = true; + try { + var type = SelectedGrammarAssembly.GetType(SelectedGrammar.TypeName, true, true); + return Activator.CreateInstance(type) as Grammar; + } + finally { + _enableAssemblyResolution = false; + } + } + + Assembly SelectedGrammarAssembly { + get { + if (SelectedGrammar == null) + return null; + + // create assembly cache entry as needed + var location = SelectedGrammar.Location; + if (!_cachedGrammarAssemblies.ContainsKey(location)) { + var fileInfo = new FileInfo(location); + _cachedGrammarAssemblies[location] = + new CachedAssembly { + LastWriteTime = fileInfo.LastWriteTime, + FileSize = fileInfo.Length, + Assembly = null + }; + + // set up file system watcher + _cachedGrammarAssemblies[location].Watcher = CreateFileWatcher(location); + } + + // get loaded assembly from cache if possible + var assembly = _cachedGrammarAssemblies[location].Assembly; + if (assembly == null) { + assembly = LoadAssembly(location); + _cachedGrammarAssemblies[location].Assembly = assembly; + } + + return assembly; + } + } + + private FileSystemWatcher CreateFileWatcher(string location) { + var folder = Path.GetDirectoryName(location); + var watcher = new FileSystemWatcher(folder); + watcher.Filter = Path.GetFileName(location); + + watcher.Changed += (s, args) => { + if (args.ChangeType != WatcherChangeTypes.Changed) + return; + + // check if assembly was changed indeed to work around multiple FileSystemWatcher event firing + var cacheEntry = _cachedGrammarAssemblies[location]; + var fileInfo = new FileInfo(location); + if (cacheEntry.LastWriteTime == fileInfo.LastWriteTime && cacheEntry.FileSize == fileInfo.Length) + return; + + // clear cached assembly and save last file update time + cacheEntry.LastWriteTime = fileInfo.LastWriteTime; + cacheEntry.FileSize = fileInfo.Length; + cacheEntry.Assembly = null; + + // delay auto-refresh for safety reasons + ThreadPool.QueueUserWorkItem(_ => { + Thread.Sleep(_autoRefreshDelay); + OnAssemblyUpdated(location); + }); + }; + + watcher.EnableRaisingEvents = true; + return watcher; + } + + private void OnAssemblyUpdated(string location) { + if (AssemblyUpdated == null || SelectedGrammar == null || SelectedGrammar.Location != location) + return; + AssemblyUpdated(this, EventArgs.Empty); + } + + public static Assembly LoadAssembly(string fileName) { + // save assembly path for dependent assemblies probing + var path = Path.GetDirectoryName(fileName); + _probingPaths.Add(path); + // 1. Assembly.Load doesn't block the file + // 2. Assembly.Load doesn't check if the assembly is already loaded in the current AppDomain + return Assembly.Load(File.ReadAllBytes(fileName)); + } + } +} diff --git a/Irony.GrammarExplorer/Highlighter/AboutCodeHighlighter.txt b/Irony.GrammarExplorer/Highlighter/AboutCodeHighlighter.txt new file mode 100644 index 0000000..0563f46 --- /dev/null +++ b/Irony.GrammarExplorer/Highlighter/AboutCodeHighlighter.txt @@ -0,0 +1 @@ +This highlighter is not a real thing, just a sketch - good enough to highlight samples in Grammar Explorer \ No newline at end of file diff --git a/Irony.GrammarExplorer/Highlighter/EditorAdapter.cs b/Irony.GrammarExplorer/Highlighter/EditorAdapter.cs new file mode 100644 index 0000000..88a1f00 --- /dev/null +++ b/Irony.GrammarExplorer/Highlighter/EditorAdapter.cs @@ -0,0 +1,144 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Runtime.InteropServices; +using System.Diagnostics; +using Irony.Parsing; + + +namespace Irony.GrammarExplorer { + + public class EditorAdapter { + Parser _parser; + Scanner _scanner; + ParseTree _parseTree; + string _newText; + EditorViewAdapterList _views = new EditorViewAdapterList(); + EditorViewAdapterList _viewsCopy; //copy used in refresh loop; set to null when views are added/removed + Thread _parserThread; + Thread _colorizerThread; + bool _stopped; + + public EditorAdapter(LanguageData language) { + _parser = new Parser(language); + _scanner = _parser.Scanner; + _colorizerThread = new Thread(ColorizerLoop); + _colorizerThread.IsBackground = true; + _parserThread = new Thread(ParserLoop); + _parserThread.IsBackground = true; + } + public void Activate() { + if ((_colorizerThread.ThreadState & System.Threading.ThreadState.Running) == 0) { + _parserThread.Start(); + _colorizerThread.Start(); + } + } + + public void Stop() { + try { + _stopped = true; + _parserThread.Join(500); + if(_parserThread.IsAlive) + _parserThread.Abort(); + _colorizerThread.Join(500); + if(_colorizerThread.IsAlive) + _colorizerThread.Abort(); + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine("Error when stopping EditorAdapter: " + ex.Message); + } + } + + public void SetNewText(string text) { + text = text ?? string.Empty; //force it to become not null; null is special value meaning "no changes" + _newText = text; + } + + public ParseTree ParseTree { + get { return _parseTree; } + } + + //Note: we don't actually parse in current version, only scan. Will implement full parsing in the future, + // to support all intellisense operations + private void ParseSource(string newText) { + //Explicitly catch the case when new text is empty + if (newText != string.Empty) { + _parseTree = _parser.Parse(newText);// .ScanOnly(newText, "Source"); + } + //notify views + var views = GetViews(); + foreach (var view in views) + view.UpdateParsedSource(_parseTree); + } + + + #region Views manipulation: AddView, RemoveView, GetViews + public void AddView(EditorViewAdapter view) { + lock (this) { + _views.Add(view); + _viewsCopy = null; + } + } + public void RemoveView(EditorViewAdapter view) { + lock (this) { + _views.Remove(view); + _viewsCopy = null; + } + } + private EditorViewAdapterList GetViews() { + EditorViewAdapterList result = _viewsCopy; + if (result == null) { + lock (this) { + _viewsCopy = new EditorViewAdapterList(); + _viewsCopy.AddRange(_views); + result = _viewsCopy; + }//lock + } + return result; + } + #endregion + + private void ParserLoop() { + while (!_stopped) { + try { + string newtext = Interlocked.Exchange(ref _newText, null); + if(newtext != null) { + ParseSource(newtext); + } + Thread.Sleep(10); + } catch(Exception ex) { + fmShowException.ShowException(ex); + System.Windows.Forms.MessageBox.Show("Fatal error in code colorizer. Colorizing had been disabled."); + _stopped = true; + } + }//while + } + + private void ColorizerLoop() { + while (!_stopped) { + EditorViewAdapterList views = GetViews(); + //Go through views and invoke refresh + foreach (EditorViewAdapter view in views) { + if (_stopped) break; + if (view.WantsColorize) + view.TryInvokeColorize(); + }//foreach + Thread.Sleep(10); + }// while !_stopped + }//method + + }//class +}//namespace diff --git a/Irony.GrammarExplorer/Highlighter/EditorViewAdapter.cs b/Irony.GrammarExplorer/Highlighter/EditorViewAdapter.cs new file mode 100644 index 0000000..05b932e --- /dev/null +++ b/Irony.GrammarExplorer/Highlighter/EditorViewAdapter.cs @@ -0,0 +1,229 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Irony.Parsing; + +namespace Irony.GrammarExplorer { + public delegate void ColorizeMethod(); + public interface IUIThreadInvoker { + void InvokeOnUIThread(ColorizeMethod colorize); + } + + public class ColorizeEventArgs : EventArgs { + public readonly TokenList Tokens; + public ColorizeEventArgs(TokenList tokens) { + Tokens = tokens; + } + } + + //Container for two numbers representing visible range of the source text (min...max) + // we use it to allow replacing two numbers in atomic operation + public class ViewRange { + public readonly int Min, Max; + public ViewRange(int min, int max) { + Min = min; + Max = max; + } + public bool Equals(ViewRange other) { + return other.Min == Min && other.Max == Max; + } + } + + public class ViewData { + // ColoredTokens + NotColoredTokens == Source.Tokens + public readonly TokenList ColoredTokens = new TokenList(); + public readonly TokenList NotColoredTokens = new TokenList(); //tokens not colored yet + public ParseTree Tree; + public ViewData(ParseTree tree) { + this.Tree = tree; + if (tree == null) return; + NotColoredTokens.AddRange(tree.Tokens); + } + } + + //Two scenarios: + // 1. Colorizing in current view range. We colorize only those tokens in current view range that were not colorized yet. + // For this we keep two lists (colorized and not colorized) tokens, and move tokens from one list to another when + // we actually colorize them. + // 2. Typing/Editing - new editor content is being pushed from EditorAdapter. We try to avoid recoloring all visible tokens, when + // user just typed a single char. What we do is try to identify "already-colored" tokens in new token list by matching + // old viewData.ColoredTokens to newly scanned token list - initially in new-viewData.NonColoredTokens. If we find a "match", + // we move the token from NonColored to Colored in new viewData. This all happens on background thread. + + public class EditorViewAdapterList : List { } + + public class EditorViewAdapter { + public readonly EditorAdapter Adapter; + private IUIThreadInvoker _invoker; + //public readonly Control Control; + ViewData _data; + ViewRange _range; + bool _wantsColorize; + int _colorizing; + public event EventHandler ColorizeTokens; + + public EditorViewAdapter(EditorAdapter adapter, IUIThreadInvoker invoker) { + Adapter = adapter; + _invoker = invoker; + Adapter.AddView(this); + _range = new ViewRange(-1, -1); + } + + //SetViewRange and SetNewText are called by text box's event handlers to notify adapter that user did something edit box + public void SetViewRange(int min, int max) { + _range = new ViewRange(min, max); + _wantsColorize = true; + } + //The new text is passed directly to EditorAdapter instance (possibly shared by several view adapters). + // EditorAdapter parses the text on a separate background thread, and notifies back this and other + // view adapters and provides them with newly parsed source through UpdateParsedSource method (see below) + public void SetNewText(string newText) { + //TODO: fix this + //hack, temp solution for more general problem + //When we load/replace/clear entire text, clear out colored tokens to force recoloring from scratch + if (string.IsNullOrEmpty(newText)) + _data = null; + Adapter.SetNewText(newText); + } + + //Called by EditorAdapter to provide the latest parsed source + public void UpdateParsedSource(ParseTree newTree) { + lock (this) { + var oldData = _data; + _data = new ViewData(newTree); + //Now try to figure out tokens that match old Colored tokens + if (oldData != null && oldData.Tree != null) { + DetectAlreadyColoredTokens(oldData.ColoredTokens, _data.Tree.SourceText.Length - oldData.Tree.SourceText.Length); + } + _wantsColorize = true; + }//lock + } + + + #region Colorizing + public bool WantsColorize { + get { return _wantsColorize; } + } + + public void TryInvokeColorize() { + if (!_wantsColorize) return; + int colorizing = Interlocked.Exchange(ref _colorizing, 1); + if (colorizing != 0) return; + _invoker.InvokeOnUIThread(Colorize); + } + private void Colorize() { + var range = _range; + var data = _data; + if (data != null) { + TokenList tokensToColor; + lock (this) { + tokensToColor = ExtractTokensInRange(data.NotColoredTokens, range.Min, range.Max); + } + if (ColorizeTokens != null && tokensToColor != null && tokensToColor.Count > 0) { + data.ColoredTokens.AddRange(tokensToColor); + ColorizeEventArgs args = new ColorizeEventArgs(tokensToColor); + ColorizeTokens(this, args); + } + }//if data != null ... + _wantsColorize = false; + _colorizing = 0; + } + + private void DetectAlreadyColoredTokens(TokenList oldColoredTokens, int shift) { + foreach (Token oldColored in oldColoredTokens) { + int index; + Token newColored; + if (FindMatchingToken(_data.NotColoredTokens, oldColored, 0, out index, out newColored) || + FindMatchingToken(_data.NotColoredTokens, oldColored, shift, out index, out newColored)) { + _data.NotColoredTokens.RemoveAt(index); + _data.ColoredTokens.Add(newColored); + } + }//foreach + } + + #endregion + + #region token utilities + private bool FindMatchingToken(TokenList inTokens, Token token, int shift, out int index, out Token result) { + index = LocateToken(inTokens, token.Location.Position + shift); + if (index >= 0) { + result = inTokens[index]; + if (TokensMatch(token, result, shift)) return true; + } + index = -1; + result = null; + return false; + } + public bool TokensMatch(Token x, Token y, int shift) { + if (x.Location.Position + shift != y.Location.Position) return false; + if (x.Terminal != y.Terminal) return false; + if (x.Text != y.Text) return false; + //Note: be careful comparing x.Value and y.Value - if value is "ValueType", it is boxed and erroneously reports non-equal + //if (x.ValueString != y.ValueString) return false; + return true; + } + public TokenList ExtractTokensInRange(TokenList tokens, int from, int until) { + TokenList result = new TokenList(); + for (int i = tokens.Count - 1; i >= 0; i--) { + var tkn = tokens[i]; + if (tkn.Location.Position > until || (tkn.Location.Position + tkn.Length < from)) continue; + result.Add(tkn); + tokens.RemoveAt(i); + } + return result; + } + + public TokenList GetTokensInRange(int from, int until) { + ViewData data = _data; + if (data == null) return null; + return GetTokensInRange(data.Tree.Tokens, from, until); + } + public TokenList GetTokensInRange(TokenList tokens, int from, int until) { + TokenList result = new TokenList(); + int fromIndex = LocateToken(tokens, from); + int untilIndex = LocateToken(tokens, until); + if (fromIndex < 0) fromIndex = 0; + if (untilIndex >= tokens.Count) untilIndex = tokens.Count - 1; + for (int i = fromIndex; i <= untilIndex; i++) { + result.Add(tokens[i]); + } + return result; + } + + //TODO: find better place for these methods + public int LocateToken(TokenList tokens, int position) { + if (tokens == null || tokens.Count == 0) return -1; + var lastToken = tokens[tokens.Count - 1]; + var lastTokenEnd = lastToken.Location.Position + lastToken.Length; + if (position < tokens[0].Location.Position || position > lastTokenEnd) return -1; + return LocateTokenExt(tokens, position, 0, tokens.Count - 1); + } + private int LocateTokenExt(TokenList tokens, int position, int fromIndex, int untilIndex) { + if (fromIndex + 1 >= untilIndex) return fromIndex; + int midIndex = (fromIndex + untilIndex) / 2; + Token middleToken = tokens[midIndex]; + if (middleToken.Location.Position <= position) + return LocateTokenExt(tokens, position, midIndex, untilIndex); + else + return LocateTokenExt(tokens, position, fromIndex, midIndex); + } + #endregion + + + }//EditorViewAdapter class + +}//namespace diff --git a/Irony.GrammarExplorer/Highlighter/RichTextBoxHighlighter.cs b/Irony.GrammarExplorer/Highlighter/RichTextBoxHighlighter.cs new file mode 100644 index 0000000..455a33a --- /dev/null +++ b/Irony.GrammarExplorer/Highlighter/RichTextBoxHighlighter.cs @@ -0,0 +1,241 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +// Aknowledgments +// This module borrows code and ideas from TinyPG framework by Herre Kuijpers, +// specifically TextMarker.cs and TextHighlighter.cs classes. +// http://www.codeproject.com/KB/recipes/TinyPG.aspx +// +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Threading; +using System.Runtime.InteropServices; +using Irony.Parsing; +using System.Diagnostics; + +namespace Irony.GrammarExplorer { + + public class TokenColorTable : Dictionary { } + + public class RichTextBoxHighlighter : NativeWindow, IDisposable, IUIThreadInvoker { + public RichTextBox TextBox; + public readonly TokenColorTable TokenColors = new TokenColorTable(); + public readonly EditorAdapter Adapter; + public readonly EditorViewAdapter ViewAdapter; + + private IntPtr _savedEventMask = IntPtr.Zero; + bool _colorizing; + bool _disposed; + + #region constructor, initialization and disposing + public RichTextBoxHighlighter(RichTextBox textBox, LanguageData language) { + TextBox = textBox; + Adapter = new EditorAdapter(language); + ViewAdapter = new EditorViewAdapter(Adapter, this); + InitColorTable(); + Connect(); + UpdateViewRange(); + ViewAdapter.SetNewText(TextBox.Text); + } + private void Connect() { + TextBox.MouseMove += TextBox_MouseMove; + TextBox.TextChanged += TextBox_TextChanged; + TextBox.KeyDown += TextBox_KeyDown; + TextBox.VScroll += TextBox_ScrollResize; + TextBox.HScroll += TextBox_ScrollResize; + TextBox.SizeChanged += TextBox_ScrollResize; + TextBox.Disposed += TextBox_Disposed; + ViewAdapter.ColorizeTokens += Adapter_ColorizeTokens; + this.AssignHandle(TextBox.Handle); + } + + private void Disconnect() { + if (TextBox != null) { + TextBox.MouseMove -= TextBox_MouseMove; + TextBox.TextChanged -= TextBox_TextChanged; + TextBox.KeyDown -= TextBox_KeyDown; + TextBox.Disposed -= TextBox_Disposed; + TextBox.VScroll -= TextBox_ScrollResize; + TextBox.HScroll -= TextBox_ScrollResize; + TextBox.SizeChanged -= TextBox_ScrollResize; + } + TextBox = null; + } + + public void Dispose() { + Adapter.Stop(); + _disposed = true; + Disconnect(); + this.ReleaseHandle(); + GC.SuppressFinalize(this); + + } + private void InitColorTable() { + TokenColors[TokenColor.Comment] = Color.Green; + TokenColors[TokenColor.Identifier] = Color.Black; + TokenColors[TokenColor.Keyword] = Color.Blue; + TokenColors[TokenColor.Number] = Color.DarkRed; + TokenColors[TokenColor.String] = Color.DarkSlateGray; + TokenColors[TokenColor.Text] = Color.Black; + + } + #endregion + + #region TextBox event handlers + + void TextBox_MouseMove(object sender, MouseEventArgs e) { + //TODO: implement showing tip + } + + void TextBox_KeyDown(object sender, KeyEventArgs e) { + //TODO: implement showing intellisense hints or drop-downs + } + + void TextBox_TextChanged(object sender, EventArgs e) { + //if we are here while colorizing, it means the "change" event is a result of our coloring action + if (_colorizing) return; + ViewAdapter.SetNewText(TextBox.Text); + } + void TextBox_ScrollResize(object sender, EventArgs e) { + UpdateViewRange(); + } + + + void TextBox_Disposed(object sender, EventArgs e) { + Dispose(); + } + private void UpdateViewRange() { + int minpos = TextBox.GetCharIndexFromPosition(new Point(0, 0)); + int maxpos = TextBox.GetCharIndexFromPosition(new Point(TextBox.ClientSize.Width, TextBox.ClientSize.Height)); + ViewAdapter.SetViewRange(minpos, maxpos); + } + #endregion + + #region WinAPI + // some winapís required + [DllImport("user32", CharSet = CharSet.Auto)] + private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern int GetScrollPos(int hWnd, int nBar); + + [DllImport("user32.dll")] + private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw); + + private const int WM_SETREDRAW = 0x000B; + private const int WM_USER = 0x400; + private const int EM_GETEVENTMASK = (WM_USER + 59); + private const int EM_SETEVENTMASK = (WM_USER + 69); + private const int SB_HORZ = 0x0; + private const int SB_VERT = 0x1; + private const int WM_HSCROLL = 0x114; + private const int WM_VSCROLL = 0x115; + private const int SB_THUMBPOSITION = 4; + const int WM_PAINT = 0x000F; + + private int HScrollPos { + get { + //sometimes explodes with null reference exception + return GetScrollPos((int)TextBox.Handle, SB_HORZ); + } + set { + SetScrollPos((IntPtr)TextBox.Handle, SB_HORZ, value, true); + PostMessageA((IntPtr)TextBox.Handle, WM_HSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0); + } + } + + private int VScrollPos { + get { + return GetScrollPos((int)TextBox.Handle, SB_VERT); + } + set { + SetScrollPos((IntPtr)TextBox.Handle, SB_VERT, value, true); + PostMessageA((IntPtr)TextBox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0); + } + } + #endregion + + #region Colorizing tokens + public void LockTextBox() { + // Stop redrawing: + SendMessage(TextBox.Handle, WM_SETREDRAW, 0, IntPtr.Zero ); + // Stop sending of events: + _savedEventMask = SendMessage(TextBox.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero); + //SendMessage(TextBox.Handle, EM_SETEVENTMASK, 0, IntPtr.Zero); + } + + public void UnlockTextBox() { + // turn on events + SendMessage(TextBox.Handle, EM_SETEVENTMASK, 0, _savedEventMask); + // turn on redrawing + SendMessage(TextBox.Handle, WM_SETREDRAW, 1, IntPtr.Zero); + } + + void Adapter_ColorizeTokens(object sender, ColorizeEventArgs args) { + if (_disposed) return; + //Debug.WriteLine("Coloring " + args.Tokens.Count + " tokens."); + _colorizing = true; + + int hscroll = HScrollPos; + int vscroll = VScrollPos; + int selstart = TextBox.SelectionStart; + int selLength = TextBox.SelectionLength; + LockTextBox(); + try { + foreach (Token tkn in args.Tokens) { + Color color = GetTokenColor(tkn); + TextBox.Select(tkn.Location.Position, tkn.Length); + TextBox.SelectionColor = color; + } + } finally { + TextBox.Select(selstart, selLength); + HScrollPos = hscroll; + VScrollPos = vscroll; + UnlockTextBox(); + _colorizing = false; + } + TextBox.Invalidate(); + } + + private Color GetTokenColor(Token token) { + if (token.EditorInfo == null) return Color.Black; + //Right now we scan source, not parse; initially all keywords are recognized as Identifiers; then they are "backpatched" + // by parser when it detects that it is in fact keyword from Grammar. So now this backpatching does not happen, + // so we have to detect keywords here + var colorIndex = token.EditorInfo.Color; + if (token.KeyTerm != null && token.KeyTerm.EditorInfo != null && token.KeyTerm.Flags.IsSet(TermFlags.IsKeyword)) { + colorIndex = token.KeyTerm.EditorInfo.Color; + }//if + Color result; + if (TokenColors.TryGetValue(colorIndex, out result)) return result; + return Color.Black; + } + #endregion + + + #region IUIThreadInvoker Members + + public void InvokeOnUIThread(ColorizeMethod colorize) { + TextBox.BeginInvoke(new MethodInvoker(colorize)); + } + + #endregion + }//class + +}//namespace diff --git a/Irony.GrammarExplorer/Program.cs b/Irony.GrammarExplorer/Program.cs new file mode 100644 index 0000000..2231e4c --- /dev/null +++ b/Irony.GrammarExplorer/Program.cs @@ -0,0 +1,63 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using System.Diagnostics; + +namespace Irony.GrammarExplorer { + class Program : MarshalByRefObject { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() { + var program = CreateInstanceInSeparateDomain(); + program.RunApplication(); + } + + static Program CreateInstanceInSeparateDomain() { + var setup = new AppDomainSetup { + ShadowCopyFiles = true.ToString() + }; + + var domain = AppDomain.CreateDomain("HostedDomain", null, setup); + return (Program)domain.CreateInstanceAndUnwrap(typeof(Program).Assembly.FullName, typeof(Program).FullName); + } + + void RunApplication() { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException); + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + Application.Run(new fmGrammarExplorer()); + } + + static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) { + fmShowException.ShowException(e.Exception); + Debug.Write("Exception!: ############################################## \n" + e.Exception.ToString()); + } + + static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { + Exception ex = e.ExceptionObject as Exception; + string message = (ex == null ? e.ExceptionObject.ToString() : ex.Message); + if (ex == null) { + Debug.Write("Exception!: ############################################## \n" + e.ExceptionObject.ToString()); + MessageBox.Show(message, "Exception"); + } else { + fmShowException.ShowException(ex); + } + } + + } +} \ No newline at end of file diff --git a/Irony.GrammarExplorer/Properties/AssemblyInfo.cs b/Irony.GrammarExplorer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d85ac39 --- /dev/null +++ b/Irony.GrammarExplorer/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Irony.GrammarExplorer")] +[assembly: AssemblyDescription("Grammar Explorer for Irony")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GrammarExplorer")] +[assembly: AssemblyCopyright("Copyright © 2010 Roman Ivantsov")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("09b192b9-9b3a-470b-a753-9f9c32bd81bc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Irony.GrammarExplorer/Properties/Resources.Designer.cs b/Irony.GrammarExplorer/Properties/Resources.Designer.cs new file mode 100644 index 0000000..9bfe972 --- /dev/null +++ b/Irony.GrammarExplorer/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Irony.GrammarExplorer.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Irony.GrammarExplorer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/Irony.GrammarExplorer/Properties/Resources.resx b/Irony.GrammarExplorer/Properties/Resources.resx new file mode 100644 index 0000000..ffecec8 --- /dev/null +++ b/Irony.GrammarExplorer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Irony.GrammarExplorer/Properties/Settings.Designer.cs b/Irony.GrammarExplorer/Properties/Settings.Designer.cs new file mode 100644 index 0000000..eaf411c --- /dev/null +++ b/Irony.GrammarExplorer/Properties/Settings.Designer.cs @@ -0,0 +1,110 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.431 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Irony.GrammarExplorer.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string SourceSample { + get { + return ((string)(this["SourceSample"])); + } + set { + this["SourceSample"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int LanguageIndex { + get { + return ((int)(this["LanguageIndex"])); + } + set { + this["LanguageIndex"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string SearchPattern { + get { + return ((string)(this["SearchPattern"])); + } + set { + this["SearchPattern"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Grammars { + get { + return ((string)(this["Grammars"])); + } + set { + this["Grammars"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool EnableTrace { + get { + return ((bool)(this["EnableTrace"])); + } + set { + this["EnableTrace"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool DisableHili { + get { + return ((bool)(this["DisableHili"])); + } + set { + this["DisableHili"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool AutoRefresh { + get { + return ((bool)(this["AutoRefresh"])); + } + set { + this["AutoRefresh"] = value; + } + } + } +} diff --git a/Irony.GrammarExplorer/Properties/Settings.settings b/Irony.GrammarExplorer/Properties/Settings.settings new file mode 100644 index 0000000..08ad2dc --- /dev/null +++ b/Irony.GrammarExplorer/Properties/Settings.settings @@ -0,0 +1,27 @@ + + + + + + + + + 0 + + + + + + + + + False + + + False + + + True + + + \ No newline at end of file diff --git a/Irony.GrammarExplorer/app.config b/Irony.GrammarExplorer/app.config new file mode 100644 index 0000000..1e5d080 --- /dev/null +++ b/Irony.GrammarExplorer/app.config @@ -0,0 +1,33 @@ + + + + +
+ + + + + + + + + 0 + + + + + + + + + False + + + False + + + True + + + + diff --git a/Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs b/Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs new file mode 100644 index 0000000..b6f33f6 --- /dev/null +++ b/Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs @@ -0,0 +1,1461 @@ +namespace Irony.GrammarExplorer { + partial class fmGrammarExplorer { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fmGrammarExplorer)); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle(); + this.tabGrammar = new System.Windows.Forms.TabControl(); + this.pageTerminals = new System.Windows.Forms.TabPage(); + this.txtTerms = new System.Windows.Forms.TextBox(); + this.pageNonTerms = new System.Windows.Forms.TabPage(); + this.txtNonTerms = new System.Windows.Forms.TextBox(); + this.pageParserStates = new System.Windows.Forms.TabPage(); + this.txtParserStates = new System.Windows.Forms.TextBox(); + this.pageTest = new System.Windows.Forms.TabPage(); + this.txtSource = new System.Windows.Forms.RichTextBox(); + this.panel1 = new System.Windows.Forms.Panel(); + this.btnLocate = new System.Windows.Forms.Button(); + this.chkDisableHili = new System.Windows.Forms.CheckBox(); + this.btnRun = new System.Windows.Forms.Button(); + this.btnFileOpen = new System.Windows.Forms.Button(); + this.btnParse = new System.Windows.Forms.Button(); + this.splitter3 = new System.Windows.Forms.Splitter(); + this.tabOutput = new System.Windows.Forms.TabControl(); + this.pageSyntaxTree = new System.Windows.Forms.TabPage(); + this.tvParseTree = new System.Windows.Forms.TreeView(); + this.pageAst = new System.Windows.Forms.TabPage(); + this.tvAst = new System.Windows.Forms.TreeView(); + this.chkParserTrace = new System.Windows.Forms.CheckBox(); + this.pnlLang = new System.Windows.Forms.Panel(); + this.chkAutoRefresh = new System.Windows.Forms.CheckBox(); + this.btnManageGrammars = new System.Windows.Forms.Button(); + this.lblSearchError = new System.Windows.Forms.Label(); + this.btnSearch = new System.Windows.Forms.Button(); + this.txtSearch = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.cboGrammars = new System.Windows.Forms.ComboBox(); + this.menuGrammars = new System.Windows.Forms.ContextMenuStrip(this.components); + this.miAdd = new System.Windows.Forms.ToolStripMenuItem(); + this.miRemove = new System.Windows.Forms.ToolStripMenuItem(); + this.miRemoveAll = new System.Windows.Forms.ToolStripMenuItem(); + this.dlgOpenFile = new System.Windows.Forms.OpenFileDialog(); + this.dlgSelectAssembly = new System.Windows.Forms.OpenFileDialog(); + this.splitBottom = new System.Windows.Forms.Splitter(); + this.tabBottom = new System.Windows.Forms.TabControl(); + this.pageLanguage = new System.Windows.Forms.TabPage(); + this.grpLanguageInfo = new System.Windows.Forms.GroupBox(); + this.label8 = new System.Windows.Forms.Label(); + this.lblParserStateCount = new System.Windows.Forms.Label(); + this.lblLanguageDescr = new System.Windows.Forms.Label(); + this.txtGrammarComments = new System.Windows.Forms.TextBox(); + this.label11 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.lblLanguageVersion = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.lblLanguage = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.lblParserConstrTime = new System.Windows.Forms.Label(); + this.pageGrammarErrors = new System.Windows.Forms.TabPage(); + this.gridGrammarErrors = new System.Windows.Forms.DataGridView(); + this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.pageParserOutput = new System.Windows.Forms.TabPage(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.gridCompileErrors = new System.Windows.Forms.DataGridView(); + this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.grpCompileInfo = new System.Windows.Forms.GroupBox(); + this.label12 = new System.Windows.Forms.Label(); + this.lblParseErrorCount = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.lblParseTime = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.lblSrcLineCount = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.lblSrcTokenCount = new System.Windows.Forms.Label(); + this.pageParserTrace = new System.Windows.Forms.TabPage(); + this.grpParserActions = new System.Windows.Forms.GroupBox(); + this.gridParserTrace = new System.Windows.Forms.DataGridView(); + this.State = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Stack = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Input = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Action = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.splitter1 = new System.Windows.Forms.Splitter(); + this.grpTokens = new System.Windows.Forms.GroupBox(); + this.lstTokens = new System.Windows.Forms.ListBox(); + this.pnlParserTraceTop = new System.Windows.Forms.Panel(); + this.chkExcludeComments = new System.Windows.Forms.CheckBox(); + this.lblTraceComment = new System.Windows.Forms.Label(); + this.pageOutput = new System.Windows.Forms.TabPage(); + this.txtOutput = new System.Windows.Forms.TextBox(); + this.pnlRuntimeInfo = new System.Windows.Forms.Panel(); + this.label14 = new System.Windows.Forms.Label(); + this.lblGCCount = new System.Windows.Forms.Label(); + this.label13 = new System.Windows.Forms.Label(); + this.lnkShowErrStack = new System.Windows.Forms.LinkLabel(); + this.lnkShowErrLocation = new System.Windows.Forms.LinkLabel(); + this.label5 = new System.Windows.Forms.Label(); + this.lblRunTime = new System.Windows.Forms.Label(); + this.toolTip = new System.Windows.Forms.ToolTip(this.components); + this.tabGrammar.SuspendLayout(); + this.pageTerminals.SuspendLayout(); + this.pageNonTerms.SuspendLayout(); + this.pageParserStates.SuspendLayout(); + this.pageTest.SuspendLayout(); + this.panel1.SuspendLayout(); + this.tabOutput.SuspendLayout(); + this.pageSyntaxTree.SuspendLayout(); + this.pageAst.SuspendLayout(); + this.pnlLang.SuspendLayout(); + this.menuGrammars.SuspendLayout(); + this.tabBottom.SuspendLayout(); + this.pageLanguage.SuspendLayout(); + this.grpLanguageInfo.SuspendLayout(); + this.pageGrammarErrors.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.gridGrammarErrors)).BeginInit(); + this.pageParserOutput.SuspendLayout(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.gridCompileErrors)).BeginInit(); + this.grpCompileInfo.SuspendLayout(); + this.pageParserTrace.SuspendLayout(); + this.grpParserActions.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.gridParserTrace)).BeginInit(); + this.grpTokens.SuspendLayout(); + this.pnlParserTraceTop.SuspendLayout(); + this.pageOutput.SuspendLayout(); + this.pnlRuntimeInfo.SuspendLayout(); + this.SuspendLayout(); + // + // tabGrammar + // + this.tabGrammar.Controls.Add(this.pageTerminals); + this.tabGrammar.Controls.Add(this.pageNonTerms); + this.tabGrammar.Controls.Add(this.pageParserStates); + this.tabGrammar.Controls.Add(this.pageTest); + this.tabGrammar.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabGrammar.Location = new System.Drawing.Point(0, 36); + this.tabGrammar.Margin = new System.Windows.Forms.Padding(4); + this.tabGrammar.Name = "tabGrammar"; + this.tabGrammar.SelectedIndex = 0; + this.tabGrammar.Size = new System.Drawing.Size(1472, 572); + this.tabGrammar.TabIndex = 0; + // + // pageTerminals + // + this.pageTerminals.Controls.Add(this.txtTerms); + this.pageTerminals.Location = new System.Drawing.Point(4, 25); + this.pageTerminals.Margin = new System.Windows.Forms.Padding(4); + this.pageTerminals.Name = "pageTerminals"; + this.pageTerminals.Padding = new System.Windows.Forms.Padding(4); + this.pageTerminals.Size = new System.Drawing.Size(1464, 543); + this.pageTerminals.TabIndex = 5; + this.pageTerminals.Text = "Terminals"; + this.pageTerminals.UseVisualStyleBackColor = true; + // + // txtTerms + // + this.txtTerms.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtTerms.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtTerms.HideSelection = false; + this.txtTerms.Location = new System.Drawing.Point(4, 4); + this.txtTerms.Margin = new System.Windows.Forms.Padding(4); + this.txtTerms.Multiline = true; + this.txtTerms.Name = "txtTerms"; + this.txtTerms.ReadOnly = true; + this.txtTerms.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtTerms.Size = new System.Drawing.Size(1456, 535); + this.txtTerms.TabIndex = 2; + // + // pageNonTerms + // + this.pageNonTerms.Controls.Add(this.txtNonTerms); + this.pageNonTerms.Location = new System.Drawing.Point(4, 25); + this.pageNonTerms.Margin = new System.Windows.Forms.Padding(4); + this.pageNonTerms.Name = "pageNonTerms"; + this.pageNonTerms.Padding = new System.Windows.Forms.Padding(4); + this.pageNonTerms.Size = new System.Drawing.Size(1464, 543); + this.pageNonTerms.TabIndex = 0; + this.pageNonTerms.Text = "Non-Terminals"; + this.pageNonTerms.UseVisualStyleBackColor = true; + // + // txtNonTerms + // + this.txtNonTerms.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtNonTerms.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtNonTerms.HideSelection = false; + this.txtNonTerms.Location = new System.Drawing.Point(4, 4); + this.txtNonTerms.Margin = new System.Windows.Forms.Padding(4); + this.txtNonTerms.Multiline = true; + this.txtNonTerms.Name = "txtNonTerms"; + this.txtNonTerms.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtNonTerms.Size = new System.Drawing.Size(1456, 535); + this.txtNonTerms.TabIndex = 1; + this.txtNonTerms.WordWrap = false; + // + // pageParserStates + // + this.pageParserStates.Controls.Add(this.txtParserStates); + this.pageParserStates.Location = new System.Drawing.Point(4, 25); + this.pageParserStates.Margin = new System.Windows.Forms.Padding(4); + this.pageParserStates.Name = "pageParserStates"; + this.pageParserStates.Padding = new System.Windows.Forms.Padding(4); + this.pageParserStates.Size = new System.Drawing.Size(1464, 543); + this.pageParserStates.TabIndex = 1; + this.pageParserStates.Text = "Parser States"; + this.pageParserStates.UseVisualStyleBackColor = true; + // + // txtParserStates + // + this.txtParserStates.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtParserStates.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtParserStates.HideSelection = false; + this.txtParserStates.Location = new System.Drawing.Point(4, 4); + this.txtParserStates.Margin = new System.Windows.Forms.Padding(4); + this.txtParserStates.Multiline = true; + this.txtParserStates.Name = "txtParserStates"; + this.txtParserStates.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtParserStates.Size = new System.Drawing.Size(1456, 535); + this.txtParserStates.TabIndex = 2; + this.txtParserStates.WordWrap = false; + // + // pageTest + // + this.pageTest.Controls.Add(this.txtSource); + this.pageTest.Controls.Add(this.panel1); + this.pageTest.Controls.Add(this.splitter3); + this.pageTest.Controls.Add(this.tabOutput); + this.pageTest.Location = new System.Drawing.Point(4, 25); + this.pageTest.Margin = new System.Windows.Forms.Padding(4); + this.pageTest.Name = "pageTest"; + this.pageTest.Padding = new System.Windows.Forms.Padding(4); + this.pageTest.Size = new System.Drawing.Size(1464, 543); + this.pageTest.TabIndex = 4; + this.pageTest.Text = "Test"; + this.pageTest.UseVisualStyleBackColor = true; + // + // txtSource + // + this.txtSource.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtSource.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtSource.HideSelection = false; + this.txtSource.Location = new System.Drawing.Point(4, 41); + this.txtSource.Margin = new System.Windows.Forms.Padding(4); + this.txtSource.Name = "txtSource"; + this.txtSource.Size = new System.Drawing.Size(981, 498); + this.txtSource.TabIndex = 22; + this.txtSource.Text = ""; + this.txtSource.TextChanged += new System.EventHandler(this.txtSource_TextChanged); + // + // panel1 + // + this.panel1.Controls.Add(this.btnLocate); + this.panel1.Controls.Add(this.chkDisableHili); + this.panel1.Controls.Add(this.btnRun); + this.panel1.Controls.Add(this.btnFileOpen); + this.panel1.Controls.Add(this.btnParse); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(4, 4); + this.panel1.Margin = new System.Windows.Forms.Padding(4); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(981, 37); + this.panel1.TabIndex = 2; + // + // btnLocate + // + this.btnLocate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnLocate.Location = new System.Drawing.Point(883, 4); + this.btnLocate.Margin = new System.Windows.Forms.Padding(4); + this.btnLocate.Name = "btnLocate"; + this.btnLocate.Size = new System.Drawing.Size(87, 28); + this.btnLocate.TabIndex = 10; + this.btnLocate.Text = "Locate >>"; + this.toolTip.SetToolTip(this.btnLocate, "Locate the source position in parse/Ast tree. "); + this.btnLocate.UseVisualStyleBackColor = true; + this.btnLocate.Click += new System.EventHandler(this.btnLocate_Click); + // + // chkDisableHili + // + this.chkDisableHili.AutoSize = true; + this.chkDisableHili.Location = new System.Drawing.Point(7, 9); + this.chkDisableHili.Margin = new System.Windows.Forms.Padding(4); + this.chkDisableHili.Name = "chkDisableHili"; + this.chkDisableHili.Size = new System.Drawing.Size(197, 21); + this.chkDisableHili.TabIndex = 9; + this.chkDisableHili.Text = "Disable syntax highlighting"; + this.chkDisableHili.UseVisualStyleBackColor = true; + this.chkDisableHili.CheckedChanged += new System.EventHandler(this.chkDisableHili_CheckedChanged); + // + // btnRun + // + this.btnRun.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnRun.Location = new System.Drawing.Point(787, 4); + this.btnRun.Margin = new System.Windows.Forms.Padding(4); + this.btnRun.Name = "btnRun"; + this.btnRun.Size = new System.Drawing.Size(87, 28); + this.btnRun.TabIndex = 7; + this.btnRun.Text = "Run"; + this.toolTip.SetToolTip(this.btnRun, "Run the source sample"); + this.btnRun.UseVisualStyleBackColor = true; + this.btnRun.Click += new System.EventHandler(this.btnRun_Click); + // + // btnFileOpen + // + this.btnFileOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnFileOpen.Location = new System.Drawing.Point(595, 4); + this.btnFileOpen.Margin = new System.Windows.Forms.Padding(4); + this.btnFileOpen.Name = "btnFileOpen"; + this.btnFileOpen.Size = new System.Drawing.Size(87, 28); + this.btnFileOpen.TabIndex = 6; + this.btnFileOpen.Text = "Load ..."; + this.toolTip.SetToolTip(this.btnFileOpen, "Load a source sample..."); + this.btnFileOpen.UseVisualStyleBackColor = true; + this.btnFileOpen.Click += new System.EventHandler(this.btnFileOpen_Click); + // + // btnParse + // + this.btnParse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnParse.Location = new System.Drawing.Point(689, 4); + this.btnParse.Margin = new System.Windows.Forms.Padding(4); + this.btnParse.Name = "btnParse"; + this.btnParse.Size = new System.Drawing.Size(89, 28); + this.btnParse.TabIndex = 1; + this.btnParse.Text = "Parse"; + this.toolTip.SetToolTip(this.btnParse, "Parse source sample"); + this.btnParse.UseVisualStyleBackColor = true; + this.btnParse.Click += new System.EventHandler(this.btnParse_Click); + // + // splitter3 + // + this.splitter3.Dock = System.Windows.Forms.DockStyle.Right; + this.splitter3.Location = new System.Drawing.Point(985, 4); + this.splitter3.Margin = new System.Windows.Forms.Padding(4); + this.splitter3.Name = "splitter3"; + this.splitter3.Size = new System.Drawing.Size(8, 535); + this.splitter3.TabIndex = 14; + this.splitter3.TabStop = false; + // + // tabOutput + // + this.tabOutput.Controls.Add(this.pageSyntaxTree); + this.tabOutput.Controls.Add(this.pageAst); + this.tabOutput.Dock = System.Windows.Forms.DockStyle.Right; + this.tabOutput.Location = new System.Drawing.Point(993, 4); + this.tabOutput.Margin = new System.Windows.Forms.Padding(4); + this.tabOutput.Name = "tabOutput"; + this.tabOutput.SelectedIndex = 0; + this.tabOutput.Size = new System.Drawing.Size(467, 535); + this.tabOutput.TabIndex = 13; + // + // pageSyntaxTree + // + this.pageSyntaxTree.Controls.Add(this.tvParseTree); + this.pageSyntaxTree.ForeColor = System.Drawing.SystemColors.ControlText; + this.pageSyntaxTree.Location = new System.Drawing.Point(4, 25); + this.pageSyntaxTree.Margin = new System.Windows.Forms.Padding(4); + this.pageSyntaxTree.Name = "pageSyntaxTree"; + this.pageSyntaxTree.Padding = new System.Windows.Forms.Padding(4); + this.pageSyntaxTree.Size = new System.Drawing.Size(459, 506); + this.pageSyntaxTree.TabIndex = 1; + this.pageSyntaxTree.Text = "Parse Tree"; + this.pageSyntaxTree.UseVisualStyleBackColor = true; + // + // tvParseTree + // + this.tvParseTree.Dock = System.Windows.Forms.DockStyle.Fill; + this.tvParseTree.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tvParseTree.HideSelection = false; + this.tvParseTree.Indent = 16; + this.tvParseTree.Location = new System.Drawing.Point(4, 4); + this.tvParseTree.Margin = new System.Windows.Forms.Padding(4); + this.tvParseTree.Name = "tvParseTree"; + this.tvParseTree.Size = new System.Drawing.Size(451, 498); + this.tvParseTree.TabIndex = 0; + this.tvParseTree.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvParseTree_AfterSelect); + // + // pageAst + // + this.pageAst.Controls.Add(this.tvAst); + this.pageAst.Location = new System.Drawing.Point(4, 25); + this.pageAst.Margin = new System.Windows.Forms.Padding(4); + this.pageAst.Name = "pageAst"; + this.pageAst.Padding = new System.Windows.Forms.Padding(4); + this.pageAst.Size = new System.Drawing.Size(459, 506); + this.pageAst.TabIndex = 0; + this.pageAst.Text = "AST"; + this.pageAst.UseVisualStyleBackColor = true; + // + // tvAst + // + this.tvAst.Dock = System.Windows.Forms.DockStyle.Fill; + this.tvAst.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tvAst.HideSelection = false; + this.tvAst.Indent = 16; + this.tvAst.Location = new System.Drawing.Point(4, 4); + this.tvAst.Margin = new System.Windows.Forms.Padding(4); + this.tvAst.Name = "tvAst"; + this.tvAst.Size = new System.Drawing.Size(451, 498); + this.tvAst.TabIndex = 1; + this.tvAst.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvAst_AfterSelect); + // + // chkParserTrace + // + this.chkParserTrace.AutoSize = true; + this.chkParserTrace.Location = new System.Drawing.Point(4, 4); + this.chkParserTrace.Margin = new System.Windows.Forms.Padding(4); + this.chkParserTrace.Name = "chkParserTrace"; + this.chkParserTrace.Size = new System.Drawing.Size(115, 21); + this.chkParserTrace.TabIndex = 0; + this.chkParserTrace.Text = "Enable Trace"; + this.chkParserTrace.UseVisualStyleBackColor = true; + // + // pnlLang + // + this.pnlLang.Controls.Add(this.chkAutoRefresh); + this.pnlLang.Controls.Add(this.btnManageGrammars); + this.pnlLang.Controls.Add(this.lblSearchError); + this.pnlLang.Controls.Add(this.btnSearch); + this.pnlLang.Controls.Add(this.txtSearch); + this.pnlLang.Controls.Add(this.label2); + this.pnlLang.Controls.Add(this.cboGrammars); + this.pnlLang.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlLang.Location = new System.Drawing.Point(0, 0); + this.pnlLang.Margin = new System.Windows.Forms.Padding(4); + this.pnlLang.Name = "pnlLang"; + this.pnlLang.Size = new System.Drawing.Size(1472, 36); + this.pnlLang.TabIndex = 13; + // + // chkAutoRefresh + // + this.chkAutoRefresh.AutoSize = true; + this.chkAutoRefresh.Checked = true; + this.chkAutoRefresh.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkAutoRefresh.Location = new System.Drawing.Point(459, 7); + this.chkAutoRefresh.Margin = new System.Windows.Forms.Padding(4); + this.chkAutoRefresh.Name = "chkAutoRefresh"; + this.chkAutoRefresh.Size = new System.Drawing.Size(109, 21); + this.chkAutoRefresh.TabIndex = 13; + this.chkAutoRefresh.Text = "Auto-refresh"; + this.toolTip.SetToolTip(this.chkAutoRefresh, resources.GetString("chkAutoRefresh.ToolTip")); + this.chkAutoRefresh.UseVisualStyleBackColor = true; + // + // btnManageGrammars + // + this.btnManageGrammars.Location = new System.Drawing.Point(408, 2); + this.btnManageGrammars.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.btnManageGrammars.Name = "btnManageGrammars"; + this.btnManageGrammars.Size = new System.Drawing.Size(37, 30); + this.btnManageGrammars.TabIndex = 12; + this.btnManageGrammars.Text = "..."; + this.btnManageGrammars.UseVisualStyleBackColor = true; + this.btnManageGrammars.Click += new System.EventHandler(this.btnManageGrammars_Click); + // + // lblSearchError + // + this.lblSearchError.AutoSize = true; + this.lblSearchError.ForeColor = System.Drawing.Color.Red; + this.lblSearchError.Location = new System.Drawing.Point(975, 11); + this.lblSearchError.Name = "lblSearchError"; + this.lblSearchError.Size = new System.Drawing.Size(70, 17); + this.lblSearchError.TabIndex = 11; + this.lblSearchError.Text = "Not found"; + this.lblSearchError.Visible = false; + // + // btnSearch + // + this.btnSearch.Location = new System.Drawing.Point(896, 5); + this.btnSearch.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.btnSearch.Name = "btnSearch"; + this.btnSearch.Size = new System.Drawing.Size(73, 28); + this.btnSearch.TabIndex = 10; + this.btnSearch.Text = "Find"; + this.btnSearch.UseVisualStyleBackColor = true; + this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click); + // + // txtSearch + // + this.txtSearch.AcceptsReturn = true; + this.txtSearch.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::Irony.GrammarExplorer.Properties.Settings.Default, "SearchPattern", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.txtSearch.Location = new System.Drawing.Point(727, 5); + this.txtSearch.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.txtSearch.Name = "txtSearch"; + this.txtSearch.Size = new System.Drawing.Size(163, 22); + this.txtSearch.TabIndex = 8; + this.txtSearch.Text = global::Irony.GrammarExplorer.Properties.Settings.Default.SearchPattern; + this.txtSearch.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtSearch_KeyPress); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(13, 7); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(71, 17); + this.label2.TabIndex = 4; + this.label2.Text = "Grammar:"; + // + // cboGrammars + // + this.cboGrammars.ContextMenuStrip = this.menuGrammars; + this.cboGrammars.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboGrammars.FormattingEnabled = true; + this.cboGrammars.Location = new System.Drawing.Point(89, 4); + this.cboGrammars.Margin = new System.Windows.Forms.Padding(4); + this.cboGrammars.Name = "cboGrammars"; + this.cboGrammars.Size = new System.Drawing.Size(311, 24); + this.cboGrammars.TabIndex = 3; + this.cboGrammars.SelectedIndexChanged += new System.EventHandler(this.cboGrammars_SelectedIndexChanged); + // + // menuGrammars + // + this.menuGrammars.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.miAdd, + this.miRemove, + this.miRemoveAll}); + this.menuGrammars.Name = "menuGrammars"; + this.menuGrammars.Size = new System.Drawing.Size(164, 70); + this.menuGrammars.Opening += new System.ComponentModel.CancelEventHandler(this.menuGrammars_Opening); + // + // miAdd + // + this.miAdd.Name = "miAdd"; + this.miAdd.Size = new System.Drawing.Size(163, 22); + this.miAdd.Text = "Add grammar..."; + this.miAdd.Click += new System.EventHandler(this.miAdd_Click); + // + // miRemove + // + this.miRemove.Name = "miRemove"; + this.miRemove.Size = new System.Drawing.Size(163, 22); + this.miRemove.Text = "Remove selected"; + this.miRemove.Click += new System.EventHandler(this.miRemove_Click); + // + // miRemoveAll + // + this.miRemoveAll.Name = "miRemoveAll"; + this.miRemoveAll.Size = new System.Drawing.Size(163, 22); + this.miRemoveAll.Text = "Remove all"; + this.miRemoveAll.Click += new System.EventHandler(this.miRemoveAll_Click); + // + // dlgSelectAssembly + // + this.dlgSelectAssembly.DefaultExt = "dll"; + this.dlgSelectAssembly.Filter = "DLL files|*.dll"; + this.dlgSelectAssembly.Title = "Select Grammar Assembly "; + // + // splitBottom + // + this.splitBottom.BackColor = System.Drawing.SystemColors.Control; + this.splitBottom.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.splitBottom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.splitBottom.Location = new System.Drawing.Point(0, 608); + this.splitBottom.Margin = new System.Windows.Forms.Padding(4); + this.splitBottom.Name = "splitBottom"; + this.splitBottom.Size = new System.Drawing.Size(1472, 6); + this.splitBottom.TabIndex = 22; + this.splitBottom.TabStop = false; + // + // tabBottom + // + this.tabBottom.Controls.Add(this.pageLanguage); + this.tabBottom.Controls.Add(this.pageGrammarErrors); + this.tabBottom.Controls.Add(this.pageParserOutput); + this.tabBottom.Controls.Add(this.pageParserTrace); + this.tabBottom.Controls.Add(this.pageOutput); + this.tabBottom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.tabBottom.Location = new System.Drawing.Point(0, 614); + this.tabBottom.Margin = new System.Windows.Forms.Padding(4); + this.tabBottom.Name = "tabBottom"; + this.tabBottom.SelectedIndex = 0; + this.tabBottom.Size = new System.Drawing.Size(1472, 230); + this.tabBottom.TabIndex = 0; + // + // pageLanguage + // + this.pageLanguage.Controls.Add(this.grpLanguageInfo); + this.pageLanguage.Location = new System.Drawing.Point(4, 25); + this.pageLanguage.Margin = new System.Windows.Forms.Padding(4); + this.pageLanguage.Name = "pageLanguage"; + this.pageLanguage.Padding = new System.Windows.Forms.Padding(4); + this.pageLanguage.Size = new System.Drawing.Size(1464, 201); + this.pageLanguage.TabIndex = 1; + this.pageLanguage.Text = "Grammar Info"; + this.pageLanguage.UseVisualStyleBackColor = true; + // + // grpLanguageInfo + // + this.grpLanguageInfo.Controls.Add(this.label8); + this.grpLanguageInfo.Controls.Add(this.lblParserStateCount); + this.grpLanguageInfo.Controls.Add(this.lblLanguageDescr); + this.grpLanguageInfo.Controls.Add(this.txtGrammarComments); + this.grpLanguageInfo.Controls.Add(this.label11); + this.grpLanguageInfo.Controls.Add(this.label9); + this.grpLanguageInfo.Controls.Add(this.lblLanguageVersion); + this.grpLanguageInfo.Controls.Add(this.label10); + this.grpLanguageInfo.Controls.Add(this.lblLanguage); + this.grpLanguageInfo.Controls.Add(this.label4); + this.grpLanguageInfo.Controls.Add(this.label6); + this.grpLanguageInfo.Controls.Add(this.lblParserConstrTime); + this.grpLanguageInfo.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpLanguageInfo.Location = new System.Drawing.Point(4, 4); + this.grpLanguageInfo.Margin = new System.Windows.Forms.Padding(4); + this.grpLanguageInfo.Name = "grpLanguageInfo"; + this.grpLanguageInfo.Padding = new System.Windows.Forms.Padding(4); + this.grpLanguageInfo.Size = new System.Drawing.Size(1456, 193); + this.grpLanguageInfo.TabIndex = 3; + this.grpLanguageInfo.TabStop = false; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(8, 139); + this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(128, 17); + this.label8.TabIndex = 26; + this.label8.Text = "Parser state count:"; + // + // lblParserStateCount + // + this.lblParserStateCount.AutoSize = true; + this.lblParserStateCount.Location = new System.Drawing.Point(223, 139); + this.lblParserStateCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParserStateCount.Name = "lblParserStateCount"; + this.lblParserStateCount.Size = new System.Drawing.Size(16, 17); + this.lblParserStateCount.TabIndex = 25; + this.lblParserStateCount.Text = "0"; + // + // lblLanguageDescr + // + this.lblLanguageDescr.Location = new System.Drawing.Point(143, 47); + this.lblLanguageDescr.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblLanguageDescr.Name = "lblLanguageDescr"; + this.lblLanguageDescr.Size = new System.Drawing.Size(817, 27); + this.lblLanguageDescr.TabIndex = 24; + this.lblLanguageDescr.Text = "(description)"; + // + // txtGrammarComments + // + this.txtGrammarComments.BackColor = System.Drawing.SystemColors.Window; + this.txtGrammarComments.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.txtGrammarComments.Location = new System.Drawing.Point(148, 78); + this.txtGrammarComments.Margin = new System.Windows.Forms.Padding(4); + this.txtGrammarComments.Multiline = true; + this.txtGrammarComments.Name = "txtGrammarComments"; + this.txtGrammarComments.ReadOnly = true; + this.txtGrammarComments.Size = new System.Drawing.Size(812, 58); + this.txtGrammarComments.TabIndex = 23; + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(8, 75); + this.label11.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(134, 17); + this.label11.TabIndex = 22; + this.label11.Text = "Grammar Comment:"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(8, 47); + this.label9.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(83, 17); + this.label9.TabIndex = 20; + this.label9.Text = "Description:"; + // + // lblLanguageVersion + // + this.lblLanguageVersion.Location = new System.Drawing.Point(371, 20); + this.lblLanguageVersion.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblLanguageVersion.Name = "lblLanguageVersion"; + this.lblLanguageVersion.Size = new System.Drawing.Size(107, 21); + this.lblLanguageVersion.TabIndex = 19; + this.lblLanguageVersion.Text = "(Version)"; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(303, 20); + this.label10.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(60, 17); + this.label10.TabIndex = 18; + this.label10.Text = "Version:"; + // + // lblLanguage + // + this.lblLanguage.Location = new System.Drawing.Point(143, 20); + this.lblLanguage.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblLanguage.Name = "lblLanguage"; + this.lblLanguage.Size = new System.Drawing.Size(307, 21); + this.lblLanguage.TabIndex = 17; + this.lblLanguage.Text = "(Language name)"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(8, 20); + this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(76, 17); + this.label4.TabIndex = 16; + this.label4.Text = "Language:"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(8, 162); + this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(191, 17); + this.label6.TabIndex = 15; + this.label6.Text = "Parser construction time, ms:"; + // + // lblParserConstrTime + // + this.lblParserConstrTime.AutoSize = true; + this.lblParserConstrTime.Location = new System.Drawing.Point(223, 162); + this.lblParserConstrTime.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParserConstrTime.Name = "lblParserConstrTime"; + this.lblParserConstrTime.Size = new System.Drawing.Size(16, 17); + this.lblParserConstrTime.TabIndex = 14; + this.lblParserConstrTime.Text = "0"; + // + // pageGrammarErrors + // + this.pageGrammarErrors.Controls.Add(this.gridGrammarErrors); + this.pageGrammarErrors.Location = new System.Drawing.Point(4, 25); + this.pageGrammarErrors.Margin = new System.Windows.Forms.Padding(4); + this.pageGrammarErrors.Name = "pageGrammarErrors"; + this.pageGrammarErrors.Padding = new System.Windows.Forms.Padding(4); + this.pageGrammarErrors.Size = new System.Drawing.Size(1464, 201); + this.pageGrammarErrors.TabIndex = 4; + this.pageGrammarErrors.Text = "Grammar Errors"; + this.pageGrammarErrors.UseVisualStyleBackColor = true; + // + // gridGrammarErrors + // + this.gridGrammarErrors.AllowUserToAddRows = false; + this.gridGrammarErrors.AllowUserToDeleteRows = false; + this.gridGrammarErrors.ColumnHeadersHeight = 24; + this.gridGrammarErrors.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing; + this.gridGrammarErrors.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.dataGridViewTextBoxColumn2, + this.dataGridViewTextBoxColumn5, + this.dataGridViewTextBoxColumn6}); + this.gridGrammarErrors.Dock = System.Windows.Forms.DockStyle.Fill; + this.gridGrammarErrors.Location = new System.Drawing.Point(4, 4); + this.gridGrammarErrors.Margin = new System.Windows.Forms.Padding(4); + this.gridGrammarErrors.MultiSelect = false; + this.gridGrammarErrors.Name = "gridGrammarErrors"; + this.gridGrammarErrors.ReadOnly = true; + this.gridGrammarErrors.RowHeadersVisible = false; + this.gridGrammarErrors.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; + this.gridGrammarErrors.Size = new System.Drawing.Size(1453, 191); + this.gridGrammarErrors.TabIndex = 3; + this.gridGrammarErrors.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridGrammarErrors_CellDoubleClick); + // + // dataGridViewTextBoxColumn2 + // + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn2.DefaultCellStyle = dataGridViewCellStyle1; + this.dataGridViewTextBoxColumn2.HeaderText = "Error Level"; + this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2"; + this.dataGridViewTextBoxColumn2.ReadOnly = true; + this.dataGridViewTextBoxColumn2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.dataGridViewTextBoxColumn2.ToolTipText = "Double-click grid cell to locate in source code"; + // + // dataGridViewTextBoxColumn5 + // + dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dataGridViewTextBoxColumn5.DefaultCellStyle = dataGridViewCellStyle2; + this.dataGridViewTextBoxColumn5.HeaderText = "Description"; + this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5"; + this.dataGridViewTextBoxColumn5.ReadOnly = true; + this.dataGridViewTextBoxColumn5.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.dataGridViewTextBoxColumn5.Width = 800; + // + // dataGridViewTextBoxColumn6 + // + this.dataGridViewTextBoxColumn6.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; + this.dataGridViewTextBoxColumn6.DataPropertyName = "State"; + dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn6.DefaultCellStyle = dataGridViewCellStyle3; + this.dataGridViewTextBoxColumn6.HeaderText = "Parser State"; + this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6"; + this.dataGridViewTextBoxColumn6.ReadOnly = true; + this.dataGridViewTextBoxColumn6.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.dataGridViewTextBoxColumn6.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.dataGridViewTextBoxColumn6.ToolTipText = "Double-click grid cell to navigate to state details"; + this.dataGridViewTextBoxColumn6.Width = 93; + // + // pageParserOutput + // + this.pageParserOutput.Controls.Add(this.groupBox1); + this.pageParserOutput.Controls.Add(this.grpCompileInfo); + this.pageParserOutput.Location = new System.Drawing.Point(4, 25); + this.pageParserOutput.Margin = new System.Windows.Forms.Padding(4); + this.pageParserOutput.Name = "pageParserOutput"; + this.pageParserOutput.Padding = new System.Windows.Forms.Padding(4); + this.pageParserOutput.Size = new System.Drawing.Size(1464, 201); + this.pageParserOutput.TabIndex = 2; + this.pageParserOutput.Text = "Parser Output"; + this.pageParserOutput.UseVisualStyleBackColor = true; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.gridCompileErrors); + this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox1.Location = new System.Drawing.Point(211, 4); + this.groupBox1.Margin = new System.Windows.Forms.Padding(4); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Padding = new System.Windows.Forms.Padding(4); + this.groupBox1.Size = new System.Drawing.Size(1249, 193); + this.groupBox1.TabIndex = 3; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Compile Errors"; + // + // gridCompileErrors + // + this.gridCompileErrors.AllowUserToAddRows = false; + this.gridCompileErrors.AllowUserToDeleteRows = false; + this.gridCompileErrors.ColumnHeadersHeight = 24; + this.gridCompileErrors.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing; + this.gridCompileErrors.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.dataGridViewTextBoxColumn3, + this.dataGridViewTextBoxColumn4, + this.dataGridViewTextBoxColumn1}); + this.gridCompileErrors.Dock = System.Windows.Forms.DockStyle.Fill; + this.gridCompileErrors.Location = new System.Drawing.Point(4, 19); + this.gridCompileErrors.Margin = new System.Windows.Forms.Padding(4); + this.gridCompileErrors.MultiSelect = false; + this.gridCompileErrors.Name = "gridCompileErrors"; + this.gridCompileErrors.ReadOnly = true; + this.gridCompileErrors.RowHeadersVisible = false; + this.gridCompileErrors.RowTemplate.Height = 24; + this.gridCompileErrors.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; + this.gridCompileErrors.Size = new System.Drawing.Size(1241, 170); + this.gridCompileErrors.TabIndex = 2; + this.gridCompileErrors.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridCompileErrors_CellDoubleClick); + // + // dataGridViewTextBoxColumn3 + // + dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn3.DefaultCellStyle = dataGridViewCellStyle4; + this.dataGridViewTextBoxColumn3.HeaderText = "L, C"; + this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3"; + this.dataGridViewTextBoxColumn3.ReadOnly = true; + this.dataGridViewTextBoxColumn3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.dataGridViewTextBoxColumn3.ToolTipText = "Double-click grid cell to locate in source code"; + this.dataGridViewTextBoxColumn3.Width = 50; + // + // dataGridViewTextBoxColumn4 + // + dataGridViewCellStyle5.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dataGridViewTextBoxColumn4.DefaultCellStyle = dataGridViewCellStyle5; + this.dataGridViewTextBoxColumn4.HeaderText = "Error Message"; + this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4"; + this.dataGridViewTextBoxColumn4.ReadOnly = true; + this.dataGridViewTextBoxColumn4.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.dataGridViewTextBoxColumn4.Width = 1000; + // + // dataGridViewTextBoxColumn1 + // + this.dataGridViewTextBoxColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; + this.dataGridViewTextBoxColumn1.DataPropertyName = "State"; + dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn1.DefaultCellStyle = dataGridViewCellStyle6; + this.dataGridViewTextBoxColumn1.HeaderText = "Parser State"; + this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1"; + this.dataGridViewTextBoxColumn1.ReadOnly = true; + this.dataGridViewTextBoxColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.dataGridViewTextBoxColumn1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.dataGridViewTextBoxColumn1.ToolTipText = "Double-click grid cell to navigate to state details"; + this.dataGridViewTextBoxColumn1.Width = 93; + // + // grpCompileInfo + // + this.grpCompileInfo.Controls.Add(this.label12); + this.grpCompileInfo.Controls.Add(this.lblParseErrorCount); + this.grpCompileInfo.Controls.Add(this.label1); + this.grpCompileInfo.Controls.Add(this.lblParseTime); + this.grpCompileInfo.Controls.Add(this.label7); + this.grpCompileInfo.Controls.Add(this.lblSrcLineCount); + this.grpCompileInfo.Controls.Add(this.label3); + this.grpCompileInfo.Controls.Add(this.lblSrcTokenCount); + this.grpCompileInfo.Dock = System.Windows.Forms.DockStyle.Left; + this.grpCompileInfo.Location = new System.Drawing.Point(4, 4); + this.grpCompileInfo.Margin = new System.Windows.Forms.Padding(4); + this.grpCompileInfo.Name = "grpCompileInfo"; + this.grpCompileInfo.Padding = new System.Windows.Forms.Padding(4); + this.grpCompileInfo.Size = new System.Drawing.Size(207, 193); + this.grpCompileInfo.TabIndex = 5; + this.grpCompileInfo.TabStop = false; + this.grpCompileInfo.Text = "Statistics"; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(16, 100); + this.label12.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(51, 17); + this.label12.TabIndex = 19; + this.label12.Text = "Errors:"; + // + // lblParseErrorCount + // + this.lblParseErrorCount.AutoSize = true; + this.lblParseErrorCount.Location = new System.Drawing.Point(144, 100); + this.lblParseErrorCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParseErrorCount.Name = "lblParseErrorCount"; + this.lblParseErrorCount.Size = new System.Drawing.Size(16, 17); + this.lblParseErrorCount.TabIndex = 18; + this.lblParseErrorCount.Text = "0"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(16, 73); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(110, 17); + this.label1.TabIndex = 17; + this.label1.Text = "Parse Time, ms:"; + // + // lblParseTime + // + this.lblParseTime.AutoSize = true; + this.lblParseTime.Location = new System.Drawing.Point(144, 73); + this.lblParseTime.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParseTime.Name = "lblParseTime"; + this.lblParseTime.Size = new System.Drawing.Size(16, 17); + this.lblParseTime.TabIndex = 16; + this.lblParseTime.Text = "0"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(16, 20); + this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(46, 17); + this.label7.TabIndex = 15; + this.label7.Text = "Lines:"; + // + // lblSrcLineCount + // + this.lblSrcLineCount.AutoSize = true; + this.lblSrcLineCount.Location = new System.Drawing.Point(144, 20); + this.lblSrcLineCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblSrcLineCount.Name = "lblSrcLineCount"; + this.lblSrcLineCount.Size = new System.Drawing.Size(16, 17); + this.lblSrcLineCount.TabIndex = 14; + this.lblSrcLineCount.Text = "0"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(16, 46); + this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(59, 17); + this.label3.TabIndex = 13; + this.label3.Text = "Tokens:"; + // + // lblSrcTokenCount + // + this.lblSrcTokenCount.AutoSize = true; + this.lblSrcTokenCount.Location = new System.Drawing.Point(144, 46); + this.lblSrcTokenCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblSrcTokenCount.Name = "lblSrcTokenCount"; + this.lblSrcTokenCount.Size = new System.Drawing.Size(16, 17); + this.lblSrcTokenCount.TabIndex = 12; + this.lblSrcTokenCount.Text = "0"; + // + // pageParserTrace + // + this.pageParserTrace.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pageParserTrace.Controls.Add(this.grpParserActions); + this.pageParserTrace.Controls.Add(this.splitter1); + this.pageParserTrace.Controls.Add(this.grpTokens); + this.pageParserTrace.Controls.Add(this.pnlParserTraceTop); + this.pageParserTrace.Location = new System.Drawing.Point(4, 25); + this.pageParserTrace.Margin = new System.Windows.Forms.Padding(4); + this.pageParserTrace.Name = "pageParserTrace"; + this.pageParserTrace.Padding = new System.Windows.Forms.Padding(4); + this.pageParserTrace.Size = new System.Drawing.Size(1464, 201); + this.pageParserTrace.TabIndex = 3; + this.pageParserTrace.Text = "Parser Trace"; + this.pageParserTrace.UseVisualStyleBackColor = true; + // + // grpParserActions + // + this.grpParserActions.Controls.Add(this.gridParserTrace); + this.grpParserActions.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpParserActions.Location = new System.Drawing.Point(4, 34); + this.grpParserActions.Margin = new System.Windows.Forms.Padding(4); + this.grpParserActions.Name = "grpParserActions"; + this.grpParserActions.Padding = new System.Windows.Forms.Padding(4); + this.grpParserActions.Size = new System.Drawing.Size(1101, 161); + this.grpParserActions.TabIndex = 4; + this.grpParserActions.TabStop = false; + // + // gridParserTrace + // + this.gridParserTrace.AllowUserToAddRows = false; + this.gridParserTrace.AllowUserToDeleteRows = false; + this.gridParserTrace.AllowUserToResizeRows = false; + this.gridParserTrace.ColumnHeadersHeight = 24; + this.gridParserTrace.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing; + this.gridParserTrace.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.State, + this.Stack, + this.Input, + this.Action}); + this.gridParserTrace.Dock = System.Windows.Forms.DockStyle.Fill; + this.gridParserTrace.Location = new System.Drawing.Point(4, 19); + this.gridParserTrace.Margin = new System.Windows.Forms.Padding(4); + this.gridParserTrace.MultiSelect = false; + this.gridParserTrace.Name = "gridParserTrace"; + this.gridParserTrace.ReadOnly = true; + this.gridParserTrace.RowHeadersVisible = false; + this.gridParserTrace.RowTemplate.Height = 24; + this.gridParserTrace.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; + this.gridParserTrace.Size = new System.Drawing.Size(1093, 138); + this.gridParserTrace.TabIndex = 0; + this.gridParserTrace.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridParserTrace_CellDoubleClick); + // + // State + // + this.State.DataPropertyName = "State"; + dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.State.DefaultCellStyle = dataGridViewCellStyle7; + this.State.HeaderText = "State"; + this.State.Name = "State"; + this.State.ReadOnly = true; + this.State.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.State.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.State.ToolTipText = "Double-click grid cell to navigate to state details"; + this.State.Width = 60; + // + // Stack + // + this.Stack.DataPropertyName = "StackTop"; + dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleRight; + this.Stack.DefaultCellStyle = dataGridViewCellStyle8; + this.Stack.HeaderText = "Stack Top"; + this.Stack.Name = "Stack"; + this.Stack.ReadOnly = true; + this.Stack.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.Stack.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.Stack.ToolTipText = "Double-click grid cell to locate node in source code"; + this.Stack.Width = 200; + // + // Input + // + this.Input.DataPropertyName = "Input"; + this.Input.HeaderText = "Input"; + this.Input.Name = "Input"; + this.Input.ReadOnly = true; + this.Input.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.Input.ToolTipText = "Double-click grid cell to locate in source code"; + this.Input.Width = 200; + // + // Action + // + this.Action.DataPropertyName = "Action"; + this.Action.HeaderText = "Action"; + this.Action.Name = "Action"; + this.Action.ReadOnly = true; + this.Action.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.Action.Width = 600; + // + // splitter1 + // + this.splitter1.BackColor = System.Drawing.SystemColors.Control; + this.splitter1.Dock = System.Windows.Forms.DockStyle.Right; + this.splitter1.Location = new System.Drawing.Point(1105, 34); + this.splitter1.Margin = new System.Windows.Forms.Padding(4); + this.splitter1.Name = "splitter1"; + this.splitter1.Size = new System.Drawing.Size(8, 161); + this.splitter1.TabIndex = 15; + this.splitter1.TabStop = false; + // + // grpTokens + // + this.grpTokens.Controls.Add(this.lstTokens); + this.grpTokens.Dock = System.Windows.Forms.DockStyle.Right; + this.grpTokens.Location = new System.Drawing.Point(1113, 34); + this.grpTokens.Margin = new System.Windows.Forms.Padding(4); + this.grpTokens.Name = "grpTokens"; + this.grpTokens.Padding = new System.Windows.Forms.Padding(4); + this.grpTokens.Size = new System.Drawing.Size(345, 161); + this.grpTokens.TabIndex = 3; + this.grpTokens.TabStop = false; + this.grpTokens.Text = "Tokens"; + // + // lstTokens + // + this.lstTokens.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstTokens.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lstTokens.FormattingEnabled = true; + this.lstTokens.ItemHeight = 17; + this.lstTokens.Location = new System.Drawing.Point(4, 19); + this.lstTokens.Margin = new System.Windows.Forms.Padding(4); + this.lstTokens.Name = "lstTokens"; + this.lstTokens.Size = new System.Drawing.Size(337, 138); + this.lstTokens.TabIndex = 2; + this.lstTokens.Click += new System.EventHandler(this.lstTokens_Click); + // + // pnlParserTraceTop + // + this.pnlParserTraceTop.BackColor = System.Drawing.SystemColors.Control; + this.pnlParserTraceTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pnlParserTraceTop.Controls.Add(this.chkExcludeComments); + this.pnlParserTraceTop.Controls.Add(this.lblTraceComment); + this.pnlParserTraceTop.Controls.Add(this.chkParserTrace); + this.pnlParserTraceTop.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlParserTraceTop.Location = new System.Drawing.Point(4, 4); + this.pnlParserTraceTop.Margin = new System.Windows.Forms.Padding(4); + this.pnlParserTraceTop.Name = "pnlParserTraceTop"; + this.pnlParserTraceTop.Size = new System.Drawing.Size(1454, 30); + this.pnlParserTraceTop.TabIndex = 1; + // + // chkExcludeComments + // + this.chkExcludeComments.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.chkExcludeComments.AutoSize = true; + this.chkExcludeComments.Checked = true; + this.chkExcludeComments.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkExcludeComments.Location = new System.Drawing.Point(1250, 4); + this.chkExcludeComments.Margin = new System.Windows.Forms.Padding(4); + this.chkExcludeComments.Name = "chkExcludeComments"; + this.chkExcludeComments.Size = new System.Drawing.Size(186, 21); + this.chkExcludeComments.TabIndex = 2; + this.chkExcludeComments.Text = "Exclude comment tokens"; + this.chkExcludeComments.UseVisualStyleBackColor = true; + // + // lblTraceComment + // + this.lblTraceComment.AutoSize = true; + this.lblTraceComment.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblTraceComment.ForeColor = System.Drawing.SystemColors.ControlDarkDark; + this.lblTraceComment.Location = new System.Drawing.Point(171, 4); + this.lblTraceComment.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblTraceComment.Name = "lblTraceComment"; + this.lblTraceComment.Size = new System.Drawing.Size(470, 17); + this.lblTraceComment.TabIndex = 1; + this.lblTraceComment.Text = "(Double-click grid cell to navigate to parser state or source code position)"; + // + // pageOutput + // + this.pageOutput.Controls.Add(this.txtOutput); + this.pageOutput.Controls.Add(this.pnlRuntimeInfo); + this.pageOutput.Location = new System.Drawing.Point(4, 25); + this.pageOutput.Margin = new System.Windows.Forms.Padding(4); + this.pageOutput.Name = "pageOutput"; + this.pageOutput.Padding = new System.Windows.Forms.Padding(4); + this.pageOutput.Size = new System.Drawing.Size(1464, 201); + this.pageOutput.TabIndex = 0; + this.pageOutput.Text = "Runtime Output"; + this.pageOutput.UseVisualStyleBackColor = true; + // + // txtOutput + // + this.txtOutput.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtOutput.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtOutput.Location = new System.Drawing.Point(4, 4); + this.txtOutput.Margin = new System.Windows.Forms.Padding(4); + this.txtOutput.Multiline = true; + this.txtOutput.Name = "txtOutput"; + this.txtOutput.ReadOnly = true; + this.txtOutput.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtOutput.Size = new System.Drawing.Size(1255, 193); + this.txtOutput.TabIndex = 1; + // + // pnlRuntimeInfo + // + this.pnlRuntimeInfo.Controls.Add(this.label14); + this.pnlRuntimeInfo.Controls.Add(this.lblGCCount); + this.pnlRuntimeInfo.Controls.Add(this.label13); + this.pnlRuntimeInfo.Controls.Add(this.lnkShowErrStack); + this.pnlRuntimeInfo.Controls.Add(this.lnkShowErrLocation); + this.pnlRuntimeInfo.Controls.Add(this.label5); + this.pnlRuntimeInfo.Controls.Add(this.lblRunTime); + this.pnlRuntimeInfo.Dock = System.Windows.Forms.DockStyle.Right; + this.pnlRuntimeInfo.Location = new System.Drawing.Point(1259, 4); + this.pnlRuntimeInfo.Margin = new System.Windows.Forms.Padding(4); + this.pnlRuntimeInfo.Name = "pnlRuntimeInfo"; + this.pnlRuntimeInfo.Size = new System.Drawing.Size(201, 193); + this.pnlRuntimeInfo.TabIndex = 2; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(8, 28); + this.label14.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(138, 17); + this.label14.TabIndex = 24; + this.label14.Text = "GC Collection Count:"; + // + // lblGCCount + // + this.lblGCCount.AutoSize = true; + this.lblGCCount.Location = new System.Drawing.Point(165, 28); + this.lblGCCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblGCCount.Name = "lblGCCount"; + this.lblGCCount.Size = new System.Drawing.Size(16, 17); + this.lblGCCount.TabIndex = 23; + this.lblGCCount.Text = "0"; + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(7, 51); + this.label13.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(99, 17); + this.label13.TabIndex = 22; + this.label13.Text = "Runtime error:"; + // + // lnkShowErrStack + // + this.lnkShowErrStack.AutoSize = true; + this.lnkShowErrStack.Enabled = false; + this.lnkShowErrStack.Location = new System.Drawing.Point(31, 106); + this.lnkShowErrStack.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lnkShowErrStack.Name = "lnkShowErrStack"; + this.lnkShowErrStack.Size = new System.Drawing.Size(101, 17); + this.lnkShowErrStack.TabIndex = 21; + this.lnkShowErrStack.TabStop = true; + this.lnkShowErrStack.Text = "Show full stack"; + this.lnkShowErrStack.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkShowErrStack_LinkClicked); + // + // lnkShowErrLocation + // + this.lnkShowErrLocation.AutoSize = true; + this.lnkShowErrLocation.Enabled = false; + this.lnkShowErrLocation.Location = new System.Drawing.Point(31, 76); + this.lnkShowErrLocation.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lnkShowErrLocation.Name = "lnkShowErrLocation"; + this.lnkShowErrLocation.Size = new System.Drawing.Size(130, 17); + this.lnkShowErrLocation.TabIndex = 20; + this.lnkShowErrLocation.TabStop = true; + this.lnkShowErrLocation.Text = "Show error location"; + this.lnkShowErrLocation.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkShowErrLocation_LinkClicked); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(7, 4); + this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(129, 17); + this.label5.TabIndex = 19; + this.label5.Text = "Execution time, ms:"; + // + // lblRunTime + // + this.lblRunTime.AutoSize = true; + this.lblRunTime.Location = new System.Drawing.Point(164, 4); + this.lblRunTime.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRunTime.Name = "lblRunTime"; + this.lblRunTime.Size = new System.Drawing.Size(16, 17); + this.lblRunTime.TabIndex = 18; + this.lblRunTime.Text = "0"; + // + // fmGrammarExplorer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1472, 844); + this.Controls.Add(this.tabGrammar); + this.Controls.Add(this.splitBottom); + this.Controls.Add(this.pnlLang); + this.Controls.Add(this.tabBottom); + this.Margin = new System.Windows.Forms.Padding(4); + this.Name = "fmGrammarExplorer"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Irony Grammar Explorer"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.fmExploreGrammar_FormClosing); + this.Load += new System.EventHandler(this.fmExploreGrammar_Load); + this.tabGrammar.ResumeLayout(false); + this.pageTerminals.ResumeLayout(false); + this.pageTerminals.PerformLayout(); + this.pageNonTerms.ResumeLayout(false); + this.pageNonTerms.PerformLayout(); + this.pageParserStates.ResumeLayout(false); + this.pageParserStates.PerformLayout(); + this.pageTest.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.tabOutput.ResumeLayout(false); + this.pageSyntaxTree.ResumeLayout(false); + this.pageAst.ResumeLayout(false); + this.pnlLang.ResumeLayout(false); + this.pnlLang.PerformLayout(); + this.menuGrammars.ResumeLayout(false); + this.tabBottom.ResumeLayout(false); + this.pageLanguage.ResumeLayout(false); + this.grpLanguageInfo.ResumeLayout(false); + this.grpLanguageInfo.PerformLayout(); + this.pageGrammarErrors.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.gridGrammarErrors)).EndInit(); + this.pageParserOutput.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.gridCompileErrors)).EndInit(); + this.grpCompileInfo.ResumeLayout(false); + this.grpCompileInfo.PerformLayout(); + this.pageParserTrace.ResumeLayout(false); + this.grpParserActions.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.gridParserTrace)).EndInit(); + this.grpTokens.ResumeLayout(false); + this.pnlParserTraceTop.ResumeLayout(false); + this.pnlParserTraceTop.PerformLayout(); + this.pageOutput.ResumeLayout(false); + this.pageOutput.PerformLayout(); + this.pnlRuntimeInfo.ResumeLayout(false); + this.pnlRuntimeInfo.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabGrammar; + private System.Windows.Forms.TabPage pageNonTerms; + private System.Windows.Forms.TabPage pageParserStates; + private System.Windows.Forms.TextBox txtNonTerms; + private System.Windows.Forms.TextBox txtParserStates; + private System.Windows.Forms.Panel pnlLang; + private System.Windows.Forms.ComboBox cboGrammars; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TabPage pageTest; + private System.Windows.Forms.Splitter splitter3; + private System.Windows.Forms.TabControl tabOutput; + private System.Windows.Forms.TabPage pageAst; + private System.Windows.Forms.TabPage pageSyntaxTree; + private System.Windows.Forms.TreeView tvParseTree; + private System.Windows.Forms.OpenFileDialog dlgOpenFile; + private System.Windows.Forms.TabPage pageTerminals; + private System.Windows.Forms.TextBox txtTerms; + private System.Windows.Forms.Button btnSearch; + private System.Windows.Forms.TextBox txtSearch; + private System.Windows.Forms.Label lblSearchError; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button btnRun; + private System.Windows.Forms.CheckBox chkParserTrace; + private System.Windows.Forms.Button btnFileOpen; + private System.Windows.Forms.Button btnParse; + private System.Windows.Forms.RichTextBox txtSource; + private System.Windows.Forms.Button btnManageGrammars; + private System.Windows.Forms.ContextMenuStrip menuGrammars; + private System.Windows.Forms.ToolStripMenuItem miAdd; + private System.Windows.Forms.ToolStripMenuItem miRemove; + private System.Windows.Forms.OpenFileDialog dlgSelectAssembly; + private System.Windows.Forms.ToolStripMenuItem miRemoveAll; + private System.Windows.Forms.TabControl tabBottom; + private System.Windows.Forms.TabPage pageOutput; + private System.Windows.Forms.TextBox txtOutput; + private System.Windows.Forms.TabPage pageLanguage; + private System.Windows.Forms.Splitter splitBottom; + private System.Windows.Forms.GroupBox grpLanguageInfo; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label lblParserConstrTime; + private System.Windows.Forms.TabPage pageParserOutput; + private System.Windows.Forms.TabPage pageParserTrace; + private System.Windows.Forms.TreeView tvAst; + private System.Windows.Forms.DataGridView gridParserTrace; + private System.Windows.Forms.GroupBox grpTokens; + private System.Windows.Forms.Panel pnlParserTraceTop; + private System.Windows.Forms.GroupBox grpParserActions; + private System.Windows.Forms.Splitter splitter1; + private System.Windows.Forms.ListBox lstTokens; + private System.Windows.Forms.Label lblTraceComment; + private System.Windows.Forms.DataGridView gridCompileErrors; + private System.Windows.Forms.CheckBox chkExcludeComments; + private System.Windows.Forms.TabPage pageGrammarErrors; + private System.Windows.Forms.DataGridView gridGrammarErrors; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label lblParseTime; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label lblSrcLineCount; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label lblSrcTokenCount; + private System.Windows.Forms.GroupBox grpCompileInfo; + private System.Windows.Forms.Label lblLanguage; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Panel pnlRuntimeInfo; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label lblRunTime; + private System.Windows.Forms.TextBox txtGrammarComments; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label lblLanguageVersion; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.Label lblParseErrorCount; + private System.Windows.Forms.Label lblLanguageDescr; + private System.Windows.Forms.LinkLabel lnkShowErrLocation; + private System.Windows.Forms.CheckBox chkDisableHili; + private System.Windows.Forms.LinkLabel lnkShowErrStack; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label lblParserStateCount; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6; + private System.Windows.Forms.CheckBox chkAutoRefresh; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.Label lblGCCount; + private System.Windows.Forms.ToolTip toolTip; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn3; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1; + private System.Windows.Forms.DataGridViewTextBoxColumn State; + private System.Windows.Forms.DataGridViewTextBoxColumn Stack; + private System.Windows.Forms.DataGridViewTextBoxColumn Input; + private System.Windows.Forms.DataGridViewTextBoxColumn Action; + private System.Windows.Forms.Button btnLocate; + + } +} + diff --git a/Irony.GrammarExplorer/fmGrammarExplorer.cs b/Irony.GrammarExplorer/fmGrammarExplorer.cs new file mode 100644 index 0000000..eed4f98 --- /dev/null +++ b/Irony.GrammarExplorer/fmGrammarExplorer.cs @@ -0,0 +1,677 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +//with contributions by Andrew Bradnan +#endregion +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using System.Diagnostics; +using System.IO; +using System.Configuration; +using System.Text.RegularExpressions; +using System.Xml; + +using Irony.Ast; +using Irony.Parsing; + +using Irony.GrammarExplorer.Properties; + +namespace Irony.GrammarExplorer { + using ScriptException = Irony.Interpreter.ScriptException; //that's the only place we use stuff from Irony.Interpreter + + public partial class fmGrammarExplorer : Form { + public fmGrammarExplorer() { + InitializeComponent(); + _grammarLoader.AssemblyUpdated += GrammarAssemblyUpdated; + } + + //fields + Grammar _grammar; + LanguageData _language; + Parser _parser; + ParseTree _parseTree; + ScriptException _runtimeError; + GrammarLoader _grammarLoader = new GrammarLoader(); + bool _loaded; + bool _treeClickDisabled; //to temporarily disable tree click when we locate the node programmatically + + #region Form load/unload events + private void fmExploreGrammar_Load(object sender, EventArgs e) { + ClearLanguageInfo(); + try { + txtSource.Text = Settings.Default.SourceSample; + txtSearch.Text = Settings.Default.SearchPattern; + GrammarItemList grammars = GrammarItemList.FromXml(Settings.Default.Grammars); + grammars.ShowIn(cboGrammars); + chkParserTrace.Checked = Settings.Default.EnableTrace; + chkDisableHili.Checked = Settings.Default.DisableHili; + chkAutoRefresh.Checked = Settings.Default.AutoRefresh; + cboGrammars.SelectedIndex = Settings.Default.LanguageIndex; //this will build parser and start colorizer + } catch { } + _loaded = true; + } + + private void fmExploreGrammar_FormClosing(object sender, FormClosingEventArgs e) { + Settings.Default.SourceSample = txtSource.Text; + Settings.Default.LanguageIndex = cboGrammars.SelectedIndex; + Settings.Default.SearchPattern = txtSearch.Text; + Settings.Default.EnableTrace = chkParserTrace.Checked; + Settings.Default.DisableHili = chkDisableHili.Checked; + Settings.Default.AutoRefresh = chkAutoRefresh.Checked; + var grammars = GrammarItemList.FromCombo(cboGrammars); + Settings.Default.Grammars = grammars.ToXml(); + Settings.Default.Save(); + }//method + #endregion + + #region Show... methods + //Show... methods ###################################################################################################################### + private void ClearLanguageInfo() { + lblLanguage.Text = string.Empty; + lblLanguageVersion.Text = string.Empty; + lblLanguageDescr.Text = string.Empty; + txtGrammarComments.Text = string.Empty; + } + + private void ClearParserOutput() { + lblSrcLineCount.Text = string.Empty; + lblSrcTokenCount.Text = ""; + lblParseTime.Text = ""; + lblParseErrorCount.Text = ""; + + lstTokens.Items.Clear(); + gridCompileErrors.Rows.Clear(); + gridParserTrace.Rows.Clear(); + lstTokens.Items.Clear(); + tvParseTree.Nodes.Clear(); + tvAst.Nodes.Clear(); + Application.DoEvents(); + } + + private void ShowLanguageInfo() { + if (_grammar == null) return; + var langAttr = LanguageAttribute.GetValue(_grammar.GetType()); + if (langAttr == null) return; + lblLanguage.Text = langAttr.LanguageName; + lblLanguageVersion.Text = langAttr.Version; + lblLanguageDescr.Text = langAttr.Description; + txtGrammarComments.Text = _grammar.GrammarComments; + } + + private void ShowCompilerErrors() { + gridCompileErrors.Rows.Clear(); + if (_parseTree == null || _parseTree.ParserMessages.Count == 0) return; + foreach (var err in _parseTree.ParserMessages) + gridCompileErrors.Rows.Add(err.Location, err, err.ParserState); + var needPageSwitch = tabBottom.SelectedTab != pageParserOutput && + !(tabBottom.SelectedTab == pageParserTrace && chkParserTrace.Checked); + if (needPageSwitch) + tabBottom.SelectedTab = pageParserOutput; + } + + private void ShowParseTrace() { + gridParserTrace.Rows.Clear(); + foreach (var entry in _parser.Context.ParserTrace) { + int index = gridParserTrace.Rows.Add(entry.State, entry.StackTop, entry.Input, entry.Message); + if (entry.IsError) + gridParserTrace.Rows[gridParserTrace.Rows.Count - 1].DefaultCellStyle.ForeColor = Color.Red; + } + //Show tokens + foreach (Token tkn in _parseTree.Tokens) { + if (chkExcludeComments.Checked && tkn.Category == TokenCategory.Comment) continue; + lstTokens.Items.Add(tkn); + } + }//method + + private void ShowCompileStats() { + if (_parseTree == null) return; + lblSrcLineCount.Text = string.Empty; + if (_parseTree.Tokens.Count > 0) + lblSrcLineCount.Text = (_parseTree.Tokens[_parseTree.Tokens.Count - 1].Location.Line + 1).ToString(); + lblSrcTokenCount.Text = _parseTree.Tokens.Count.ToString(); + lblParseTime.Text = _parseTree.ParseTimeMilliseconds.ToString(); + lblParseErrorCount.Text = _parseTree.ParserMessages.Count.ToString(); + Application.DoEvents(); + //Note: this time is "pure" parse time; actual delay after cliking "Compile" includes time to fill ParseTree, AstTree controls + } + + private void ShowParseTree() { + tvParseTree.Nodes.Clear(); + if (_parseTree == null) return; + AddParseNodeRec(null, _parseTree.Root); + } + private void AddParseNodeRec(TreeNode parent, ParseTreeNode node) { + if (node == null) return; + string txt = node.ToString(); + TreeNode tvNode = (parent == null? tvParseTree.Nodes.Add(txt) : parent.Nodes.Add(txt) ); + tvNode.Tag = node; + foreach(var child in node.ChildNodes) + AddParseNodeRec(tvNode, child); + } + + private void ShowAstTree() { + tvAst.Nodes.Clear(); + if (_parseTree == null || _parseTree.Root == null || _parseTree.Root.AstNode == null) return; + AddAstNodeRec(null, _parseTree.Root.AstNode); + } + + private void AddAstNodeRec(TreeNode parent, object astNode) { + if (astNode == null) return; + string txt = astNode.ToString(); + TreeNode newNode = (parent == null ? + tvAst.Nodes.Add(txt) : parent.Nodes.Add(txt)); + newNode.Tag = astNode; + var iBrowsable = astNode as IBrowsableAstNode; + if (iBrowsable == null) return; + var childList = iBrowsable.GetChildNodes(); + foreach (var child in childList) + AddAstNodeRec(newNode, child); + } + + private void ShowParserConstructionResults() { + lblParserStateCount.Text = _language.ParserData.States.Count.ToString(); + lblParserConstrTime.Text = _language.ConstructionTime.ToString(); + txtParserStates.Text = string.Empty; + gridGrammarErrors.Rows.Clear(); + txtTerms.Text = string.Empty; + txtNonTerms.Text = string.Empty; + txtParserStates.Text = string.Empty; + tabBottom.SelectedTab = pageLanguage; + if (_parser == null) return; + txtTerms.Text = ParserDataPrinter.PrintTerminals(_language); + txtNonTerms.Text = ParserDataPrinter.PrintNonTerminals(_language); + txtParserStates.Text = ParserDataPrinter.PrintStateList(_language); + ShowGrammarErrors(); + }//method + + private void ShowGrammarErrors() { + gridGrammarErrors.Rows.Clear(); + var errors = _parser.Language.Errors; + if (errors.Count == 0) return; + foreach (var err in errors) + gridGrammarErrors.Rows.Add(err.Level.ToString(), err.Message, err.State); + if (tabBottom.SelectedTab != pageGrammarErrors) + tabBottom.SelectedTab = pageGrammarErrors; + } + + private void ShowSourcePosition(int position, int length) { + if (position < 0) return; + txtSource.SelectionStart = position; + txtSource.SelectionLength = length; + //txtSource.Select(location.Position, length); + txtSource.ScrollToCaret(); + if (tabGrammar.SelectedTab != pageTest) + tabGrammar.SelectedTab = pageTest; + txtSource.Focus(); + //lblLoc.Text = location.ToString(); + } + private void ShowSourcePositionAndTraceToken(int position, int length) { + ShowSourcePosition(position, length); + //find token in trace + for (int i = 0; i < lstTokens.Items.Count; i++) { + var tkn = lstTokens.Items[i] as Token; + if (tkn.Location.Position == position) { + lstTokens.SelectedIndex = i; + return; + }//if + }//for i + } + private void LocateParserState(ParserState state) { + if (state == null) return; + if (tabGrammar.SelectedTab != pageParserStates) + tabGrammar.SelectedTab = pageParserStates; + //first scroll to the bottom, so that scrolling to needed position brings it to top + txtParserStates.SelectionStart = txtParserStates.Text.Length - 1; + txtParserStates.ScrollToCaret(); + DoSearch(txtParserStates, "State " + state.Name, 0); + } + + private void ShowRuntimeError(ScriptException error){ + _runtimeError = error; + lnkShowErrLocation.Enabled = _runtimeError != null; + lnkShowErrStack.Enabled = lnkShowErrLocation.Enabled; + if (_runtimeError != null) { + //the exception was caught and processed by Interpreter + WriteOutput("Error: " + error.Message + " At " + _runtimeError.Location.ToUiString() + "."); + ShowSourcePosition(_runtimeError.Location.Position, 1); + } else { + //the exception was not caught by interpreter/AST node. Show full exception info + WriteOutput("Error: " + error.Message); + fmShowException.ShowException(error); + + } + tabBottom.SelectedTab = pageOutput; + } + + private void SelectTreeNode(TreeView tree, TreeNode node) { + _treeClickDisabled = true; + tree.SelectedNode = node; + if (node != null) + node.EnsureVisible(); + _treeClickDisabled = false; + } + + + + private void ClearRuntimeInfo() { + lnkShowErrLocation.Enabled = false; + lnkShowErrStack.Enabled = false; + _runtimeError = null; + txtOutput.Text = string.Empty; + } + + #endregion + + #region Grammar combo menu commands + private void menuGrammars_Opening(object sender, CancelEventArgs e) { + miRemove.Enabled = cboGrammars.Items.Count > 0; + } + + private void miAdd_Click(object sender, EventArgs e) { + if (dlgSelectAssembly.ShowDialog() != DialogResult.OK) return; + string location = dlgSelectAssembly.FileName; + if (string.IsNullOrEmpty(location)) return; + var oldGrammars = new GrammarItemList(); + foreach(var item in cboGrammars.Items) + oldGrammars.Add((GrammarItem) item); + var grammars = fmSelectGrammars.SelectGrammars(location, oldGrammars); + if (grammars == null) return; + foreach (GrammarItem item in grammars) + cboGrammars.Items.Add(item); + // auto-select the first grammar if no grammar currently selected + if (cboGrammars.SelectedIndex < 0 && grammars.Count > 0) + cboGrammars.SelectedIndex = 0; + } + + private void miRemove_Click(object sender, EventArgs e) { + if (MessageBox.Show("Are you sure you want to remove grammmar " + cboGrammars.SelectedItem + "?", + "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { + cboGrammars.Items.RemoveAt(cboGrammars.SelectedIndex); + _parser = null; + if (cboGrammars.Items.Count > 0) + cboGrammars.SelectedIndex = 0; + } + } + + private void miRemoveAll_Click(object sender, EventArgs e) { + if (MessageBox.Show("Are you sure you want to remove all grammmars in the list?", + "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { + cboGrammars.Items.Clear(); + _parser = null; + } + } + #endregion + + #region Parsing and running + private void CreateGrammar() { + _grammar = _grammarLoader.CreateGrammar(); + } + + private void CreateParser() { + StopHighlighter(); + btnRun.Enabled = false; + txtOutput.Text = string.Empty; + _parseTree = null; + + btnRun.Enabled = _grammar is ICanRunSample; + _language = new LanguageData(_grammar); + _parser = new Parser (_language); + ShowParserConstructionResults(); + StartHighlighter(); + } + + private void ParseSample() { + ClearParserOutput(); + if (_parser == null || !_parser.Language.CanParse()) return; + _parseTree = null; + GC.Collect(); //to avoid disruption of perf times with occasional collections + _parser.Context.TracingEnabled = chkParserTrace.Checked; + try { + _parser.Parse(txtSource.Text, ""); + } catch (Exception ex) { + gridCompileErrors.Rows.Add(null, ex.Message, null); + tabBottom.SelectedTab = pageParserOutput; + throw; + } finally { + _parseTree = _parser.Context.CurrentParseTree; + ShowCompilerErrors(); + if (chkParserTrace.Checked) { + ShowParseTrace(); + } + ShowCompileStats(); + ShowParseTree(); + ShowAstTree(); + } + } + + private void RunSample() { + ClearRuntimeInfo(); + Stopwatch sw = new Stopwatch(); + int oldGcCount; + txtOutput.Text = ""; + try { + if (_parseTree == null) + ParseSample(); + if (_parseTree.ParserMessages.Count > 0) return; + + GC.Collect(); //to avoid disruption of perf times with occasional collections + oldGcCount = GC.CollectionCount(0); + System.Threading.Thread.Sleep(100); + + sw.Start(); + var iRunner = _grammar as ICanRunSample; + var args = new RunSampleArgs(_language, txtSource.Text, _parseTree); + string output = iRunner.RunSample(args); + sw.Stop(); + lblRunTime.Text = sw.ElapsedMilliseconds.ToString(); + var gcCount = GC.CollectionCount(0) - oldGcCount; + lblGCCount.Text = gcCount.ToString(); + WriteOutput(output); + tabBottom.SelectedTab = pageOutput; + } catch (ScriptException ex) { + ShowRuntimeError(ex); + } finally { + sw.Stop(); + }//finally + }//method + + private void WriteOutput(string text) { + if (string.IsNullOrEmpty(text)) return; + txtOutput.Text += text + Environment.NewLine; + txtOutput.Select(txtOutput.Text.Length - 1, 0); + } + + #endregion + + #region miscellaneous: LoadSourceFile, Search, Source highlighting + private void LoadSourceFile(string path) { + _parseTree = null; + StreamReader reader = null; + try { + reader = new StreamReader(path); + txtSource.Text = null; //to clear any old formatting + txtSource.Text = reader.ReadToEnd(); + txtSource.Select(0, 0); + + } catch (Exception e) { + MessageBox.Show(e.Message); + } finally { + if (reader != null) + reader.Close(); + } + } + + //Source highlighting + RichTextBoxHighlighter _highlighter; + private void StartHighlighter() { + if (_highlighter != null) + StopHighlighter(); + if (chkDisableHili.Checked) return; + if (!_parser.Language.CanParse()) return; + _highlighter = new RichTextBoxHighlighter(txtSource, _language); + _highlighter.Adapter.Activate(); + } + private void StopHighlighter() { + if (_highlighter == null) return; + _highlighter.Dispose(); + _highlighter = null; + ClearHighlighting(); + } + private void ClearHighlighting() { + var txt = txtSource.Text; + txtSource.Clear(); + txtSource.Text = txt; //remove all old highlighting + } + private void EnableHighlighter(bool enable) { + if (_highlighter != null) + StopHighlighter(); + if (enable) + StartHighlighter(); + } + + //The following methods are contributed by Andrew Bradnan; pasted here with minor changes + private void DoSearch() { + lblSearchError.Visible = false; + TextBoxBase textBox = GetSearchContentBox(); + if (textBox == null) return; + int idxStart = textBox.SelectionStart + textBox.SelectionLength; + if (!DoSearch(textBox, txtSearch.Text, idxStart)) { + lblSearchError.Text = "Not found."; + lblSearchError.Visible = true; + } + }//method + + private bool DoSearch(TextBoxBase textBox, string fragment, int start) { + textBox.SelectionLength = 0; + // Compile the regular expression. + Regex r = new Regex(fragment, RegexOptions.IgnoreCase); + // Match the regular expression pattern against a text string. + Match m = r.Match(textBox.Text.Substring(start)); + if (m.Success) { + int i = 0; + Group g = m.Groups[i]; + CaptureCollection cc = g.Captures; + Capture c = cc[0]; + textBox.SelectionStart = c.Index + start; + textBox.SelectionLength = c.Length; + textBox.Focus(); + textBox.ScrollToCaret(); + return true; + } + return false; + }//method + + public TextBoxBase GetSearchContentBox() { + switch (tabGrammar.SelectedIndex) { + case 0: + return txtTerms; + case 1: + return txtNonTerms; + case 2: + return txtParserStates; + case 4: + return txtSource; + default: + return null; + }//switch + } + + #endregion + + #region Controls event handlers + //Controls event handlers ################################################################################################### + private void btnParse_Click(object sender, EventArgs e) { + ParseSample(); + } + + private void btnRun_Click(object sender, EventArgs e) { + RunSample(); + } + + private void tvParseTree_AfterSelect(object sender, TreeViewEventArgs e) { + if (_treeClickDisabled) + return; + var vtreeNode = tvParseTree.SelectedNode; + if (vtreeNode == null) return; + var parseNode = vtreeNode.Tag as ParseTreeNode; + if (parseNode == null) return; + ShowSourcePosition(parseNode.Span.Location.Position, 1); + } + + private void tvAst_AfterSelect(object sender, TreeViewEventArgs e) { + if (_treeClickDisabled) + return; + var treeNode = tvAst.SelectedNode; + if (treeNode == null) return; + var iBrowsable = treeNode.Tag as IBrowsableAstNode; + if (iBrowsable == null) return; + ShowSourcePosition(iBrowsable.Position, 1); + } + + bool _changingGrammar; + private void LoadSelectedGrammar() { + try { + ClearLanguageInfo(); + ClearParserOutput(); + ClearRuntimeInfo(); + + _changingGrammar = true; + CreateGrammar(); + ShowLanguageInfo(); + CreateParser(); + } finally { + _changingGrammar = false; //in case of exception + } + } + + private void cboGrammars_SelectedIndexChanged(object sender, EventArgs e) { + _grammarLoader.SelectedGrammar = cboGrammars.SelectedItem as GrammarItem; + LoadSelectedGrammar(); + } + + private void GrammarAssemblyUpdated(object sender, EventArgs args) { + if (InvokeRequired) { + Invoke(new EventHandler(GrammarAssemblyUpdated), sender, args); + return; + } + if (chkAutoRefresh.Checked) { + LoadSelectedGrammar(); + txtGrammarComments.Text += String.Format("{0}Grammar assembly reloaded: {1:HH:mm:ss}", Environment.NewLine, DateTime.Now); + } + } + + private void btnFileOpen_Click(object sender, EventArgs e) { + if (dlgOpenFile.ShowDialog() != DialogResult.OK) return; + ClearParserOutput(); + LoadSourceFile(dlgOpenFile.FileName); + } + + private void txtSource_TextChanged(object sender, EventArgs e) { + _parseTree = null; //force it to recompile on run + } + + private void btnManageGrammars_Click(object sender, EventArgs e) { + menuGrammars.Show(btnManageGrammars, 0, btnManageGrammars.Height); + } + + private void cboParseMethod_SelectedIndexChanged(object sender, EventArgs e) { + //changing grammar causes setting of parse method combo, so to prevent double-call to ConstructParser + // we don't do it here if _changingGrammar is set + if (!_changingGrammar) + CreateParser(); + } + + private void gridParserTrace_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { + if (_parser.Context == null || e.RowIndex < 0 || e.RowIndex >= _parser.Context.ParserTrace.Count) return; + var entry = _parser.Context.ParserTrace[e.RowIndex]; + switch (e.ColumnIndex) { + case 0: //state + case 3: //action + LocateParserState(entry.State); + break; + case 1: //stack top + if (entry.StackTop != null) + ShowSourcePositionAndTraceToken(entry.StackTop.Span.Location.Position, entry.StackTop.Span.Length); + break; + case 2: //input + if (entry.Input != null) + ShowSourcePositionAndTraceToken(entry.Input.Span.Location.Position, entry.Input.Span.Length); + break; + }//switch + } + + private void lstTokens_Click(object sender, EventArgs e) { + if (lstTokens.SelectedIndex < 0) + return; + Token token = (Token)lstTokens.SelectedItem; + ShowSourcePosition(token.Location.Position, token.Length); + } + + private void gridCompileErrors_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { + if (e.RowIndex < 0 || e.RowIndex >= gridCompileErrors.Rows.Count) return; + var err = gridCompileErrors.Rows[e.RowIndex].Cells[1].Value as LogMessage; + switch (e.ColumnIndex) { + case 0: //state + case 1: //stack top + ShowSourcePosition(err.Location.Position, 1); + break; + case 2: //input + if (err.ParserState != null) + LocateParserState(err.ParserState); + break; + }//switch + } + + private void gridGrammarErrors_CellDoubleClick(object sender, DataGridViewCellEventArgs e) { + if (e.RowIndex < 0 || e.RowIndex >= gridGrammarErrors.Rows.Count) return; + var state = gridGrammarErrors.Rows[e.RowIndex].Cells[2].Value as ParserState; + if (state != null) + LocateParserState(state); + } + + private void btnSearch_Click(object sender, EventArgs e) { + DoSearch(); + }//method + + private void txtSearch_KeyPress(object sender, KeyPressEventArgs e) { + if (e.KeyChar == '\r') // key + DoSearch(); + } + + private void lnkShowErrLocation_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { + if (_runtimeError != null) + ShowSourcePosition(_runtimeError.Location.Position, 1); + } + + private void lnkShowErrStack_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { + if (_runtimeError == null) return; + if (_runtimeError.InnerException != null) + fmShowException.ShowException(_runtimeError.InnerException); + else + fmShowException.ShowException(_runtimeError); + } + + private void btnLocate_Click(object sender, EventArgs e) { + if (_parseTree == null) + ParseSample(); + var p = txtSource.SelectionStart; + tvParseTree.SelectedNode = null; //just in case we won't find + tvAst.SelectedNode = null; + SelectTreeNode(tvParseTree, LocateTreeNode(tvParseTree.Nodes, p, node => (node.Tag as ParseTreeNode).Span.Location.Position)); + SelectTreeNode(tvAst, LocateTreeNode(tvAst.Nodes, p, node => (node.Tag as IBrowsableAstNode).Position)); + txtSource.Focus(); //set focus back to source + } + + private TreeNode LocateTreeNode(TreeNodeCollection nodes, int position, Func positionFunction) { + TreeNode current = null; + //Find the last node in the list that is "before or at" the position + foreach (TreeNode node in nodes) { + if (positionFunction(node) > position) break; //from loop + current = node; + } + //if current has children, search them + if (current != null && current.Nodes.Count > 0) + current = LocateTreeNode(current.Nodes, position, positionFunction) ?? current; + return current; + } + + private void chkDisableHili_CheckedChanged(object sender, EventArgs e) { + if (!_loaded) return; + EnableHighlighter(!chkDisableHili.Checked); + } + + #endregion + + }//class +} \ No newline at end of file diff --git a/Irony.GrammarExplorer/fmGrammarExplorer.resx b/Irony.GrammarExplorer/fmGrammarExplorer.resx new file mode 100644 index 0000000..8b472b6 --- /dev/null +++ b/Irony.GrammarExplorer/fmGrammarExplorer.resx @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 454, 17 + + + If you check this box, the Grammar Explorer will automatically reload the grammar definition +from the containing assembly whenever the target assembly is updated (recompiled). +Note that assemblies are never unloaded from memory, so after multiple recompiles +you may end up with some memory occupied with outdated assemblies. +Simply restart the Grammar Explorer to clean-up the memory. + + + 135, 17 + + + 17, 17 + + + 274, 17 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 45 + + \ No newline at end of file diff --git a/Irony.GrammarExplorer/fmSelectGrammars.Designer.cs b/Irony.GrammarExplorer/fmSelectGrammars.Designer.cs new file mode 100644 index 0000000..3051271 --- /dev/null +++ b/Irony.GrammarExplorer/fmSelectGrammars.Designer.cs @@ -0,0 +1,127 @@ +namespace Irony.GrammarExplorer { + partial class fmSelectGrammars { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.pnlBottom = new System.Windows.Forms.Panel(); + this.btnUncheckAll = new System.Windows.Forms.Button(); + this.btnCheckAll = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.lstGrammars = new System.Windows.Forms.CheckedListBox(); + this.pnlBottom.SuspendLayout(); + this.SuspendLayout(); + // + // pnlBottom + // + this.pnlBottom.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.pnlBottom.Controls.Add(this.btnUncheckAll); + this.pnlBottom.Controls.Add(this.btnCheckAll); + this.pnlBottom.Controls.Add(this.btnCancel); + this.pnlBottom.Controls.Add(this.btnOK); + this.pnlBottom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlBottom.Location = new System.Drawing.Point(0, 245); + this.pnlBottom.Name = "pnlBottom"; + this.pnlBottom.Size = new System.Drawing.Size(451, 35); + this.pnlBottom.TabIndex = 1; + // + // btnUncheckAll + // + this.btnUncheckAll.Location = new System.Drawing.Point(75, 3); + this.btnUncheckAll.Name = "btnUncheckAll"; + this.btnUncheckAll.Size = new System.Drawing.Size(74, 24); + this.btnUncheckAll.TabIndex = 3; + this.btnUncheckAll.Text = "Uncheck All"; + this.btnUncheckAll.UseVisualStyleBackColor = true; + this.btnUncheckAll.Click += new System.EventHandler(this.btnCheckUncheck_Click); + // + // btnCheckAll + // + this.btnCheckAll.Location = new System.Drawing.Point(3, 3); + this.btnCheckAll.Name = "btnCheckAll"; + this.btnCheckAll.Size = new System.Drawing.Size(66, 24); + this.btnCheckAll.TabIndex = 2; + this.btnCheckAll.Text = "Check All"; + this.btnCheckAll.UseVisualStyleBackColor = true; + this.btnCheckAll.Click += new System.EventHandler(this.btnCheckUncheck_Click); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(379, 3); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(66, 24); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // btnOK + // + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(307, 3); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(66, 24); + this.btnOK.TabIndex = 0; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + // + // lstGrammars + // + this.lstGrammars.Dock = System.Windows.Forms.DockStyle.Fill; + this.lstGrammars.FormattingEnabled = true; + this.lstGrammars.Location = new System.Drawing.Point(0, 0); + this.lstGrammars.Name = "lstGrammars"; + this.lstGrammars.Size = new System.Drawing.Size(451, 244); + this.lstGrammars.Sorted = true; + this.lstGrammars.TabIndex = 2; + // + // fmSelectGrammars + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(451, 280); + this.Controls.Add(this.lstGrammars); + this.Controls.Add(this.pnlBottom); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "fmSelectGrammars"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Select Grammars"; + this.pnlBottom.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Panel pnlBottom; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.CheckedListBox lstGrammars; + private System.Windows.Forms.Button btnUncheckAll; + private System.Windows.Forms.Button btnCheckAll; + } +} \ No newline at end of file diff --git a/Irony.GrammarExplorer/fmSelectGrammars.cs b/Irony.GrammarExplorer/fmSelectGrammars.cs new file mode 100644 index 0000000..8dac7ae --- /dev/null +++ b/Irony.GrammarExplorer/fmSelectGrammars.cs @@ -0,0 +1,94 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Reflection; +using Irony.Parsing; +using System.IO; + +namespace Irony.GrammarExplorer { + public partial class fmSelectGrammars : Form { + public fmSelectGrammars() { + InitializeComponent(); + } + + public static GrammarItemList SelectGrammars(string assemblyPath, GrammarItemList loadedGrammars) { + var fromGrammars = LoadGrammars(assemblyPath); + if (fromGrammars == null) + return null; + //fill the listbox and show the form + fmSelectGrammars form = new fmSelectGrammars(); + var listbox = form.lstGrammars; + listbox.Sorted = false; + foreach(GrammarItem item in fromGrammars) { + listbox.Items.Add(item); + if (!ContainsGrammar(loadedGrammars, item)) + listbox.SetItemChecked(listbox.Items.Count - 1, true); + } + listbox.Sorted = true; + + if (form.ShowDialog() != DialogResult.OK) return null; + GrammarItemList result = new GrammarItemList(); + for (int i = 0; i < listbox.Items.Count; i++) { + if (listbox.GetItemChecked(i)) { + var item = listbox.Items[i] as GrammarItem; + item._loading = false; + result.Add(item); + } + } + return result; + } + + private static GrammarItemList LoadGrammars(string assemblyPath) { + Assembly asm = null; + try { + asm = GrammarLoader.LoadAssembly(assemblyPath); + } catch (Exception ex) { + MessageBox.Show("Failed to load assembly: " + ex.Message); + return null; + } + var types = asm.GetTypes(); + var grammars = new GrammarItemList(); + foreach (Type t in types) { + if (t.IsAbstract) continue; + if (!t.IsSubclassOf(typeof(Grammar))) continue; + grammars.Add(new GrammarItem(t, assemblyPath)); + } + if (grammars.Count == 0) { + MessageBox.Show("No classes derived from Irony.Grammar were found in the assembly."); + return null; + } + return grammars; + } + + private static bool ContainsGrammar(GrammarItemList items, GrammarItem item) { + foreach (var listItem in items) + if (listItem.TypeName == item.TypeName && listItem.Location == item.Location) + return true; + return false; + } + + private void btnCheckUncheck_Click(object sender, EventArgs e) { + bool check = sender == btnCheckAll; + for (int i = 0; i < lstGrammars.Items.Count; i++) + lstGrammars.SetItemChecked(i, check); + } + + }//class +} diff --git a/Irony.GrammarExplorer/fmSelectGrammars.resx b/Irony.GrammarExplorer/fmSelectGrammars.resx new file mode 100644 index 0000000..ff31a6d --- /dev/null +++ b/Irony.GrammarExplorer/fmSelectGrammars.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Irony.GrammarExplorer/fmShowException.Designer.cs b/Irony.GrammarExplorer/fmShowException.Designer.cs new file mode 100644 index 0000000..29b8b32 --- /dev/null +++ b/Irony.GrammarExplorer/fmShowException.Designer.cs @@ -0,0 +1,61 @@ +namespace Irony.GrammarExplorer { + partial class fmShowException { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.txtException = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // txtException + // + this.txtException.AcceptsReturn = true; + this.txtException.AcceptsTab = true; + this.txtException.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtException.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtException.HideSelection = false; + this.txtException.Location = new System.Drawing.Point(0, 0); + this.txtException.Multiline = true; + this.txtException.Name = "txtException"; + this.txtException.ReadOnly = true; + this.txtException.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtException.Size = new System.Drawing.Size(764, 334); + this.txtException.TabIndex = 1; + // + // fmShowException + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(764, 334); + this.Controls.Add(this.txtException); + this.Name = "fmShowException"; + this.Text = "Exception"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox txtException; + } +} \ No newline at end of file diff --git a/Irony.GrammarExplorer/fmShowException.cs b/Irony.GrammarExplorer/fmShowException.cs new file mode 100644 index 0000000..f3d88a0 --- /dev/null +++ b/Irony.GrammarExplorer/fmShowException.cs @@ -0,0 +1,33 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +namespace Irony.GrammarExplorer { + public partial class fmShowException : Form { + public fmShowException() { + InitializeComponent(); + } + public static void ShowException(Exception ex) { + fmShowException fm = new fmShowException(); + fm.txtException.Text = ex.ToString(); + fm.txtException.Select(0, 0); + fm.Show(); + } + } +} \ No newline at end of file diff --git a/Irony.GrammarExplorer/fmShowException.resx b/Irony.GrammarExplorer/fmShowException.resx new file mode 100644 index 0000000..ff31a6d --- /dev/null +++ b/Irony.GrammarExplorer/fmShowException.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Irony.Interpreter/015.Irony.Interpreter.2010.csproj b/Irony.Interpreter/015.Irony.Interpreter.2010.csproj new file mode 100644 index 0000000..2a2849d --- /dev/null +++ b/Irony.Interpreter/015.Irony.Interpreter.2010.csproj @@ -0,0 +1,133 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {321A7F5D-00C2-4095-9970-075CDEE8C139} + Library + Properties + Irony.Interpreter + Irony.Interpreter + v4.0 + 512 + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + irony.snk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + + + + \ No newline at end of file diff --git a/Irony.Interpreter/Ast/AstContext/InterpreterAstContext.cs b/Irony.Interpreter/Ast/AstContext/InterpreterAstContext.cs new file mode 100644 index 0000000..df6f0cb --- /dev/null +++ b/Irony.Interpreter/Ast/AstContext/InterpreterAstContext.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + //Extension of AstContext + public class InterpreterAstContext : AstContext { + public readonly OperatorHandler OperatorHandler; + + public InterpreterAstContext(LanguageData language, OperatorHandler operatorHandler = null) : base(language) { + OperatorHandler = operatorHandler ?? new OperatorHandler(language.Grammar.CaseSensitive); + base.DefaultIdentifierNodeType = typeof(IdentifierNode); + base.DefaultLiteralNodeType = typeof(LiteralValueNode); + base.DefaultNodeType = null; + } + + }//class +}//ns diff --git a/Irony.Interpreter/Ast/AstContext/OperatorHandler.cs b/Irony.Interpreter/Ast/AstContext/OperatorHandler.cs new file mode 100644 index 0000000..6358d13 --- /dev/null +++ b/Irony.Interpreter/Ast/AstContext/OperatorHandler.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + + public class OperatorInfo { + public string Symbol; + public ExpressionType ExpressionType; + public int Precedence; + public Associativity Associativity; + } + + public class OperatorInfoDictionary : Dictionary { + public OperatorInfoDictionary(bool caseSensitive) : base(caseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase) { } + + public void Add(string symbol, ExpressionType expressionType, int precedence, Associativity associativity = Associativity.Left) { + var info = new OperatorInfo() { + Symbol = symbol, ExpressionType = expressionType, + Precedence = precedence, Associativity = associativity + }; + this[symbol] = info; + } + }//class + + + public class OperatorHandler { + private OperatorInfoDictionary _registeredOperators; + + + public OperatorHandler(bool languageCaseSensitive) { + _registeredOperators = new OperatorInfoDictionary(languageCaseSensitive); + BuildDefaultOperatorMappings(); + } + + public ExpressionType GetOperatorExpressionType(string symbol) { + OperatorInfo opInfo; + if (_registeredOperators.TryGetValue(symbol, out opInfo)) + return opInfo.ExpressionType; + return CustomExpressionTypes.NotAnExpression; + } + + public virtual ExpressionType GetUnaryOperatorExpressionType(string symbol) { + switch (symbol.ToLowerInvariant()) { + case "+": return ExpressionType.UnaryPlus; + case "-": return ExpressionType.Negate; + case "!": + case "not": + case "~": + return ExpressionType.Not; + default: + return CustomExpressionTypes.NotAnExpression; + } + } + + + public virtual ExpressionType GetBinaryOperatorForAugmented(ExpressionType augmented) { + switch(augmented) { + case ExpressionType.AddAssign: + case ExpressionType.AddAssignChecked: + return ExpressionType.AddChecked; + case ExpressionType.AndAssign: + return ExpressionType.And; + case ExpressionType.Decrement: + return ExpressionType.SubtractChecked; + case ExpressionType.DivideAssign: + return ExpressionType.Divide; + case ExpressionType.ExclusiveOrAssign: + return ExpressionType.ExclusiveOr; + case ExpressionType.LeftShiftAssign: + return ExpressionType.LeftShift; + case ExpressionType.ModuloAssign: + return ExpressionType.Modulo; + case ExpressionType.MultiplyAssign: + case ExpressionType.MultiplyAssignChecked: + return ExpressionType.MultiplyChecked; + case ExpressionType.OrAssign: + return ExpressionType.Or; + case ExpressionType.RightShiftAssign: + return ExpressionType.RightShift; + case ExpressionType.SubtractAssign: + case ExpressionType.SubtractAssignChecked: + return ExpressionType.SubtractChecked; + default: + return CustomExpressionTypes.NotAnExpression; + } + } + + public virtual OperatorInfoDictionary BuildDefaultOperatorMappings() { + var dict = _registeredOperators; + dict.Clear(); + int p = 0; //precedence + + p += 10; + dict.Add("=", ExpressionType.Assign, p); + dict.Add("+=", ExpressionType.AddAssignChecked, p); + dict.Add("-=", ExpressionType.SubtractAssignChecked, p); + dict.Add("*=", ExpressionType.MultiplyAssignChecked, p); + dict.Add("/=", ExpressionType.DivideAssign, p); + dict.Add("%=", ExpressionType.ModuloAssign, p); + dict.Add("|=", ExpressionType.OrAssign, p); + dict.Add("&=", ExpressionType.AndAssign, p); + dict.Add("^=", ExpressionType.ExclusiveOrAssign, p); + + p += 10; + dict.Add("==", ExpressionType.Equal, p); + dict.Add("!=", ExpressionType.NotEqual, p); + dict.Add("<>", ExpressionType.NotEqual, p); + + p += 10; + dict.Add("<", ExpressionType.LessThan, p); + dict.Add("<=", ExpressionType.LessThanOrEqual, p); + dict.Add(">", ExpressionType.GreaterThan, p); + dict.Add(">=", ExpressionType.GreaterThanOrEqual, p); + + p += 10; + dict.Add("|", ExpressionType.Or, p); + dict.Add("or", ExpressionType.Or, p); + dict.Add("||", ExpressionType.OrElse, p); + dict.Add("orelse", ExpressionType.OrElse, p); + dict.Add("^", ExpressionType.ExclusiveOr, p); + dict.Add("xor", ExpressionType.ExclusiveOr, p); + + p += 10; + dict.Add("&", ExpressionType.And, p); + dict.Add("and", ExpressionType.And, p); + dict.Add("&&", ExpressionType.AndAlso, p); + dict.Add("andalso", ExpressionType.AndAlso, p); + + p += 10; + dict.Add("!", ExpressionType.Not, p); + dict.Add("not", ExpressionType.Not, p); + + p += 10; + dict.Add("<<", ExpressionType.LeftShift, p); + dict.Add(">>", ExpressionType.RightShift, p); + + p += 10; + dict.Add("+", ExpressionType.AddChecked, p); + dict.Add("-", ExpressionType.SubtractChecked, p); + + p += 10; + dict.Add("*", ExpressionType.MultiplyChecked, p); + dict.Add("/", ExpressionType.Divide, p); + dict.Add("%", ExpressionType.Modulo, p); + dict.Add("**", ExpressionType.Power, p); + + p += 10; + dict.Add("??", ExpressionType.Coalesce, p); + dict.Add("?", ExpressionType.Conditional, p); + + return dict; + }//method + + } + + +} diff --git a/Irony.Interpreter/Ast/Base/AstInterfaces.cs b/Irony.Interpreter/Ast/Base/AstInterfaces.cs new file mode 100644 index 0000000..b416592 --- /dev/null +++ b/Irony.Interpreter/Ast/Base/AstInterfaces.cs @@ -0,0 +1,42 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Linq.Expressions; + +namespace Irony.Interpreter.Ast{ + + //This interface is expected by Irony's Gramamr Explorer. + public interface ICallTarget { + object Call(ScriptThread thread, object[] parameters); + } + + //Simple visitor interface + public interface IAstVisitor { + void BeginVisit(IVisitableNode node); + void EndVisit(IVisitableNode node); + } + + public interface IVisitableNode { + void AcceptVisitor(IAstVisitor visitor); + } + + public interface IOperatorHelper { + ExpressionType GetOperatorExpressionType(string symbol); + ExpressionType GetUnaryOperatorExpressionType(string symbol); + + } +} diff --git a/Irony.Interpreter/Ast/Base/AstNode.cs b/Irony.Interpreter/Ast/Base/AstNode.cs new file mode 100644 index 0000000..da2134e --- /dev/null +++ b/Irony.Interpreter/Ast/Base/AstNode.cs @@ -0,0 +1,190 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; +using System.CodeDom; +using System.Xml; +using System.IO; + +using Irony.Ast; +using Irony.Parsing; +using Irony.Interpreter; + +namespace Irony.Interpreter.Ast { + + public static class CustomExpressionTypes { + public const ExpressionType NotAnExpression =(ExpressionType) (-1); + } + + public class AstNodeList : List { } + + //Base AST node class + public partial class AstNode : IAstNodeInit, IBrowsableAstNode, IVisitableNode { + public AstNode Parent; + public BnfTerm Term; + public SourceSpan Span { get; set; } + public AstNodeFlags Flags; + protected ExpressionType ExpressionType = CustomExpressionTypes.NotAnExpression; + protected object LockObject = new object(); + + //Used for pointing to error location. For most nodes it would be the location of the node itself. + // One exception is BinExprNode: when we get "Division by zero" error evaluating + // x = (5 + 3) / (2 - 2) + // it is better to point to "/" as error location, rather than the first "(" - which is the start + // location of binary expression. + public SourceLocation ErrorAnchor; + //UseType is set by parent + public NodeUseType UseType = NodeUseType.Unknown; + // Role is a free-form string used as prefix in ToString() representation of the node. + // Node's parent can set it to "property name" or role of the child node in parent's node currentFrame.Context. + public string Role; + // Default AstNode.ToString() returns 'Role: AsString', which is used for showing node in AST tree. + public string AsString { get; protected set; } + public readonly AstNodeList ChildNodes = new AstNodeList(); //List of child nodes + + //Reference to Evaluate method implementation. Initially set to DoEvaluate virtual method. + public EvaluateMethod Evaluate; + public ValueSetterMethod SetValue; + + // Public default constructor + public AstNode() { + this.Evaluate = DoEvaluate; + this.SetValue = DoSetValue; + } + public SourceLocation Location { get { return Span.Location; } } + + #region IAstNodeInit Members + public virtual void Init(AstContext context, ParseTreeNode treeNode) { + this.Term = treeNode.Term; + Span = treeNode.Span; + ErrorAnchor = this.Location; + treeNode.AstNode = this; + AsString = (Term == null ? this.GetType().Name : Term.Name); + } + #endregion + + //ModuleNode - computed on demand + public AstNode ModuleNode { + get { + if (_moduleNode == null) { + _moduleNode = (Parent == null) ? this : Parent.ModuleNode; + } + return _moduleNode; + } + set { _moduleNode = value; } + } AstNode _moduleNode; + + + #region virtual methods: DoEvaluate, SetValue, IsConstant, SetIsTail, GetDependentScopeInfo + public virtual void Reset() { + _moduleNode = null; + Evaluate = DoEvaluate; + foreach (var child in ChildNodes) + child.Reset(); + } + + //By default the Evaluate field points to this method. + protected virtual object DoEvaluate(ScriptThread thread) { + //These 2 lines are standard prolog/epilog statements. Place them in every Evaluate and SetValue implementations. + thread.CurrentNode = this; //standard prolog + thread.CurrentNode = Parent; //standard epilog + return null; + } + + public virtual void DoSetValue(ScriptThread thread, object value) { + //Place the prolog/epilog lines in every implementation of SetValue method (see DoEvaluate above) + } + + public virtual bool IsConstant() { + return false; + } + + /// + /// Sets a flag indicating that the node is in tail position. The value is propagated from parent to children. + /// Should propagate this call to appropriate children. + /// + public virtual void SetIsTail() { + Flags |= AstNodeFlags.IsTail; + } + + /// + /// Dependent scope is a scope produced by the node. For ex, FunctionDefNode defines a scope + /// + public virtual ScopeInfo DependentScopeInfo { + get {return _dependentScope; } + set { _dependentScope = value; } + } ScopeInfo _dependentScope; + + #endregion + + #region IBrowsableAstNode Members + public virtual System.Collections.IEnumerable GetChildNodes() { + return ChildNodes; + } + public int Position { + get { return Span.Location.Position; } + } + #endregion + + #region Visitors, Iterators + //the first primitive Visitor facility + public virtual void AcceptVisitor(IAstVisitor visitor) { + visitor.BeginVisit(this); + if (ChildNodes.Count > 0) + foreach(AstNode node in ChildNodes) + node.AcceptVisitor(visitor); + visitor.EndVisit(this); + } + + //Node traversal + public IEnumerable GetAll() { + AstNodeList result = new AstNodeList(); + AddAll(result); + return result; + } + private void AddAll(AstNodeList list) { + list.Add(this); + foreach (AstNode child in this.ChildNodes) + if (child != null) + child.AddAll(list); + } + #endregion + + #region overrides: ToString + public override string ToString() { + return string.IsNullOrEmpty(Role) ? AsString : Role + ": " + AsString; + } + #endregion + + #region Utility methods: AddChild, HandleError + + protected AstNode AddChild(string role, ParseTreeNode childParseNode) { + return AddChild(NodeUseType.Unknown, role, childParseNode); + } + + protected AstNode AddChild(NodeUseType useType, string role, ParseTreeNode childParseNode) { + var child = (AstNode)childParseNode.AstNode; + if (child == null) + child = new NullNode(childParseNode.Term); //put a stub to throw an exception with clear message on attempt to evaluate. + child.Role = role; + child.Parent = this; + ChildNodes.Add(child); + return child; + } + + #endregion + + }//class +}//namespace diff --git a/Irony.Interpreter/Ast/Base/BasicTypes.cs b/Irony.Interpreter/Ast/Base/BasicTypes.cs new file mode 100644 index 0000000..5a3f1e8 --- /dev/null +++ b/Irony.Interpreter/Ast/Base/BasicTypes.cs @@ -0,0 +1,43 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter.Ast { + + public delegate object EvaluateMethod(ScriptThread thread); + public delegate void ValueSetterMethod(ScriptThread thread, object value); + + [Flags] + public enum AstNodeFlags { + None = 0x0, + IsTail = 0x01, //the node is in tail position + //IsScope = 0x02, //node defines scope for local variables + } + + [Flags] + public enum NodeUseType { + Unknown, + Name, //identifier used as a Name container - system would not use it's Evaluate method directly + CallTarget, + ValueRead, + ValueWrite, + ValueReadWrite, + Parameter, + Keyword, + SpecialSymbol, + } + +} diff --git a/Irony.Interpreter/Ast/Expressions/BinaryOperationNode.cs b/Irony.Interpreter/Ast/Expressions/BinaryOperationNode.cs new file mode 100644 index 0000000..4b5687a --- /dev/null +++ b/Irony.Interpreter/Ast/Expressions/BinaryOperationNode.cs @@ -0,0 +1,129 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Reflection; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + public class BinaryOperationNode : AstNode { + public AstNode Left, Right; + public string OpSymbol; + public ExpressionType Op; + private OperatorImplementation _lastUsed; + private object _constValue; + private int _failureCount; + + public BinaryOperationNode() { } + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + Left = AddChild("Arg", nodes[0]); + Right = AddChild("Arg", nodes[2]); + var opToken = nodes[1].FindToken(); + OpSymbol = opToken.Text; + var ictxt = context as InterpreterAstContext; + Op = ictxt.OperatorHandler.GetOperatorExpressionType(OpSymbol); + // Set error anchor to operator, so on error (Division by zero) the explorer will point to + // operator node as location, not to the very beginning of the first operand. + ErrorAnchor = opToken.Location; + AsString = Op + "(operator)"; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + //assign implementation method + switch (Op) { + case ExpressionType.AndAlso: + this.Evaluate = EvaluateAndAlso; + break; + case ExpressionType.OrElse: + this.Evaluate = EvaluateOrElse; + break; + default: + this.Evaluate = DefaultEvaluateImplementation; + break; + } + // actually evaluate and get the result. + var result = Evaluate(thread); + // Check if result is constant - if yes, save the value and switch to method that directly returns the result. + if (IsConstant()) { + _constValue = result; + AsString = Op + "(operator) Const=" + _constValue; + this.Evaluate = EvaluateConst; + } + thread.CurrentNode = Parent; //standard epilog + return result; + } + + private object EvaluateAndAlso(ScriptThread thread) { + var leftValue = Left.Evaluate(thread); + if (!thread.Runtime.IsTrue(leftValue)) return leftValue; //if false return immediately + return Right.Evaluate(thread); + } + private object EvaluateOrElse(ScriptThread thread) { + var leftValue = Left.Evaluate(thread); + if (thread.Runtime.IsTrue(leftValue)) return leftValue; + return Right.Evaluate(thread); + } + + protected object EvaluateFast(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var arg1 = Left.Evaluate(thread); + var arg2 = Right.Evaluate(thread); + //If we have _lastUsed, go straight for it; if types mismatch it will throw + if (_lastUsed != null) { + try { + var res = _lastUsed.EvaluateBinary(arg1, arg2); + thread.CurrentNode = Parent; //standard epilog + return res; + } catch { + _lastUsed = null; + _failureCount++; + // if failed 3 times, change to method without direct try + if (_failureCount > 3) + Evaluate = DefaultEvaluateImplementation; + } //catch + }// if _lastUsed + // go for normal evaluation + var result = thread.Runtime.ExecuteBinaryOperator(this.Op, arg1, arg2, ref _lastUsed); + thread.CurrentNode = Parent; //standard epilog + return result; + }//method + + protected object DefaultEvaluateImplementation(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var arg1 = Left.Evaluate(thread); + var arg2 = Right.Evaluate(thread); + var result = thread.Runtime.ExecuteBinaryOperator(this.Op, arg1, arg2, ref _lastUsed); + thread.CurrentNode = Parent; //standard epilog + return result; + }//method + + private object EvaluateConst(ScriptThread thread) { + return _constValue; + } + + public override bool IsConstant() { + if (_isConstant) return true; + _isConstant = Left.IsConstant() && Right.IsConstant(); + return _isConstant; + } bool _isConstant; + }//class +}//namespace diff --git a/Irony.Interpreter/Ast/Expressions/ExpressionListNode.cs b/Irony.Interpreter/Ast/Expressions/ExpressionListNode.cs new file mode 100644 index 0000000..ce0916f --- /dev/null +++ b/Irony.Interpreter/Ast/Expressions/ExpressionListNode.cs @@ -0,0 +1,46 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + //A node representing expression list - for example, list of argument expressions in function call + public class ExpressionListNode : AstNode { + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + foreach (var child in treeNode.ChildNodes) { + AddChild(NodeUseType.Parameter, "expr", child); + } + AsString = "Expression list"; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var values = new object[ChildNodes.Count]; + for (int i = 0; i < values.Length; i++) { + values[i] = ChildNodes[i].Evaluate(thread); + } + thread.CurrentNode = Parent; //standard epilog + return values; + } + + }//class + +}//namespace diff --git a/Irony.Interpreter/Ast/Expressions/IfNode.cs b/Irony.Interpreter/Ast/Expressions/IfNode.cs new file mode 100644 index 0000000..6eb85c0 --- /dev/null +++ b/Irony.Interpreter/Ast/Expressions/IfNode.cs @@ -0,0 +1,61 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + public class IfNode : AstNode { + public AstNode Test; + public AstNode IfTrue; + public AstNode IfFalse; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + Test = AddChild("Test", nodes[0]); + IfTrue = AddChild("IfTrue", nodes[1]); + if (nodes.Count > 2) + IfFalse = AddChild("IfFalse", nodes[2]); + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + object result = null; + var test = Test.Evaluate(thread); + var isTrue = thread.Runtime.IsTrue(test); + if (isTrue) { + if (IfTrue != null) + result = IfTrue.Evaluate(thread); + } else { + if (IfFalse != null) + result = IfFalse.Evaluate(thread); + } + thread.CurrentNode = Parent; //standard epilog + return result; + } + + public override void SetIsTail() { + base.SetIsTail(); + if (IfTrue != null) + IfTrue.SetIsTail(); + if (IfFalse != null) + IfFalse.SetIsTail(); + } + + }//class + +}//namespace diff --git a/Irony.Interpreter/Ast/Expressions/IncDecNode.cs b/Irony.Interpreter/Ast/Expressions/IncDecNode.cs new file mode 100644 index 0000000..0af17d1 --- /dev/null +++ b/Irony.Interpreter/Ast/Expressions/IncDecNode.cs @@ -0,0 +1,68 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + public class IncDecNode : AstNode { + public bool IsPostfix; + public string OpSymbol; + public string BinaryOpSymbol; //corresponding binary operation: + for ++, - for -- + public ExpressionType BinaryOp; + public AstNode Argument; + private OperatorImplementation _lastUsed; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + FindOpAndDetectPostfix(nodes); + int argIndex = IsPostfix? 0 : 1; + Argument = AddChild(NodeUseType.ValueReadWrite, "Arg", nodes[argIndex]); + BinaryOpSymbol = OpSymbol[0].ToString(); //take a single char out of ++ or -- + var interpContext = (InterpreterAstContext)context; + BinaryOp = interpContext.OperatorHandler.GetOperatorExpressionType(BinaryOpSymbol); + base.AsString = OpSymbol + (IsPostfix ? "(postfix)" : "(prefix)"); + } + + private void FindOpAndDetectPostfix(ParseTreeNodeList mappedNodes) { + IsPostfix = false; //assume it + OpSymbol = mappedNodes[0].FindTokenAndGetText(); + if (OpSymbol == "--" || OpSymbol == "++") return; + IsPostfix = true; + OpSymbol = mappedNodes[1].FindTokenAndGetText(); + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var oldValue = Argument.Evaluate(thread); + var newValue = thread.Runtime.ExecuteBinaryOperator(BinaryOp, oldValue, 1, ref _lastUsed); + Argument.SetValue(thread, newValue); + var result = IsPostfix ? oldValue : newValue; + thread.CurrentNode = Parent; //standard epilog + return result; + } + + public override void SetIsTail() { + base.SetIsTail(); + Argument.SetIsTail(); + } + }//class + +} diff --git a/Irony.Interpreter/Ast/Expressions/IndexedAccessNode.cs b/Irony.Interpreter/Ast/Expressions/IndexedAccessNode.cs new file mode 100644 index 0000000..e01e56d --- /dev/null +++ b/Irony.Interpreter/Ast/Expressions/IndexedAccessNode.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + public class IndexedAccessNode : AstNode { + AstNode _target, _index; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + _target = AddChild("Target", nodes.First()); + _index = AddChild("Index", nodes.Last()); + AsString = "[" + _index + "]"; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + object result = null; + var targetValue = _target.Evaluate(thread); + if (targetValue == null) + thread.ThrowScriptError("Target object is null."); + var type = targetValue.GetType(); + var indexValue = _index.Evaluate(thread); + //string and array are special cases + if (type == typeof(string)) { + var sTarget = targetValue as string; + var iIndex = Convert.ToInt32(indexValue); + result = sTarget[iIndex]; + } else if (type.IsArray) { + var arr = targetValue as Array; + var iIndex = Convert.ToInt32(indexValue); + result = arr.GetValue(iIndex); + } else if (targetValue is IDictionary) { + var dict = (IDictionary)targetValue; + result = dict[indexValue]; + } else { + const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.InvokeMethod; + result = type.InvokeMember("get_Item", flags, null, targetValue, new object[] { indexValue }); + } + thread.CurrentNode = Parent; //standard epilog + return result; + } + + public override void DoSetValue(ScriptThread thread, object value) { + thread.CurrentNode = this; //standard prolog + var targetValue = _target.Evaluate(thread); + if (targetValue == null) + thread.ThrowScriptError("Target object is null."); + var type = targetValue.GetType(); + var indexValue = _index.Evaluate(thread); + //string and array are special cases + if (type == typeof(string)) { + thread.ThrowScriptError("String is read-only."); + } else if (type.IsArray) { + var arr = targetValue as Array; + var iIndex = Convert.ToInt32(indexValue); + arr.SetValue(value, iIndex); + } else if (targetValue is IDictionary) { + var dict = (IDictionary)targetValue; + dict[indexValue] = value; + } else { + const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.InvokeMethod; + type.InvokeMember("set_Item", flags, null, targetValue, new object[] { indexValue, value }); + } + thread.CurrentNode = Parent; //standard epilog + }//method + + }//class + + +}//namespace diff --git a/Irony.Interpreter/Ast/Expressions/MemberAccessNode.cs b/Irony.Interpreter/Ast/Expressions/MemberAccessNode.cs new file mode 100644 index 0000000..49b58a5 --- /dev/null +++ b/Irony.Interpreter/Ast/Expressions/MemberAccessNode.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + //For now we do not support dotted namespace/type references like System.Collections or System.Collections.List. + // Only references to objects like 'objFoo.Name' or 'objFoo.DoStuff()' + public class MemberAccessNode : AstNode { + AstNode _left; + string _memberName; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + _left = AddChild("Target", nodes[0]); + var right = nodes[nodes.Count - 1]; + _memberName = right.FindTokenAndGetText(); + ErrorAnchor = right.Span.Location; + AsString = "." + _memberName; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + object result = null; + var leftValue = _left.Evaluate(thread); + if (leftValue == null) + thread.ThrowScriptError("Target object is null."); + var type = leftValue.GetType(); + var members = type.GetMember(_memberName); + if (members == null || members.Length == 0) + thread.ThrowScriptError("Member {0} not found in object of type {1}.", _memberName, type); + var member = members[0]; + switch (member.MemberType) { + case MemberTypes.Property: + var propInfo = member as PropertyInfo; + result = propInfo.GetValue(leftValue, null); + break; + case MemberTypes.Field: + var fieldInfo = member as FieldInfo; + result = fieldInfo.GetValue(leftValue); + break; + case MemberTypes.Method: + result = new ClrMethodBindingTargetInfo(type, _memberName, leftValue); //this bindingInfo works as a call target + break; + default: + thread.ThrowScriptError("Invalid member type ({0}) for member {1} of type {2}.", member.MemberType, _memberName, type); + result = null; + break; + }//switch + thread.CurrentNode = Parent; //standard epilog + return result; + } + + public override void DoSetValue(ScriptThread thread, object value) { + thread.CurrentNode = this; //standard prolog + var leftValue = _left.Evaluate(thread); + if (leftValue == null) + thread.ThrowScriptError("Target object is null."); + var type = leftValue.GetType(); + var members = type.GetMember(_memberName); + if (members == null || members.Length == 0) + thread.ThrowScriptError("Member {0} not found in object of type {1}.", _memberName, type); + var member = members[0]; + switch (member.MemberType) { + case MemberTypes.Property: + var propInfo = member as PropertyInfo; + propInfo.SetValue(leftValue, value, null); + break; + case MemberTypes.Field: + var fieldInfo = member as FieldInfo; + fieldInfo.SetValue(leftValue, value); + break; + default: + thread.ThrowScriptError("Cannot assign to member {0} of type {1}.", _memberName, type); + break; + }//switch + thread.CurrentNode = Parent; //standard epilog + }//method + + }//class + + +}//namespace diff --git a/Irony.Interpreter/Ast/Expressions/UnaryOperationNode.cs b/Irony.Interpreter/Ast/Expressions/UnaryOperationNode.cs new file mode 100644 index 0000000..e3d99a9 --- /dev/null +++ b/Irony.Interpreter/Ast/Expressions/UnaryOperationNode.cs @@ -0,0 +1,52 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + public class UnaryOperationNode : AstNode { + public string OpSymbol; + public AstNode Argument; + private OperatorImplementation _lastUsed; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + OpSymbol = nodes[0].FindTokenAndGetText(); + Argument = AddChild("Arg", nodes[1]); + base.AsString = OpSymbol + "(unary op)"; + var interpContext = (InterpreterAstContext)context; + base.ExpressionType = interpContext.OperatorHandler.GetUnaryOperatorExpressionType(OpSymbol); + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var arg = Argument.Evaluate(thread); + var result = thread.Runtime.ExecuteUnaryOperator(base.ExpressionType, arg, ref _lastUsed); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + public override void SetIsTail() { + base.SetIsTail(); + Argument.SetIsTail(); + } + + }//class +}//namespace diff --git a/Irony.Interpreter/Ast/Functions/Closure.cs b/Irony.Interpreter/Ast/Functions/Closure.cs new file mode 100644 index 0000000..b38f2d6 --- /dev/null +++ b/Irony.Interpreter/Ast/Functions/Closure.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter.Ast { + public class Closure : ICallTarget { + //The scope that created closure - used as Creator property of new scope, and will be used to find Parents (enclosing scopes) + public Scope CreatorScope; + public LambdaNode Lamda; + public Closure(Scope parentScope, LambdaNode targetNode) { + CreatorScope = parentScope; + Lamda = targetNode; + } + + public object Call(ScriptThread thread, object[] parameters) { + return Lamda.Call(CreatorScope, thread, parameters); + } + + public override string ToString() { + return Lamda.ToString(); //returns nice string like "" + } + + } //class +} diff --git a/Irony.Interpreter/Ast/Functions/FunctionCallNode.cs b/Irony.Interpreter/Ast/Functions/FunctionCallNode.cs new file mode 100644 index 0000000..e3d05b7 --- /dev/null +++ b/Irony.Interpreter/Ast/Functions/FunctionCallNode.cs @@ -0,0 +1,130 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + //A node representing function call. Also handles Special Forms + public class FunctionCallNode : AstNode { + AstNode TargetRef; + AstNode Arguments; + string _targetName; + SpecialForm _specialForm; + AstNode[] _specialFormArgs; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + TargetRef = AddChild("Target", nodes[0]); + TargetRef.UseType = NodeUseType.CallTarget; + _targetName = nodes[0].FindTokenAndGetText(); + Arguments = AddChild("Args", nodes[1]); + AsString = "Call " + _targetName; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + SetupEvaluateMethod(thread); + var result = Evaluate(thread); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + private void SetupEvaluateMethod(ScriptThread thread) { + var languageTailRecursive = thread.Runtime.Language.Grammar.LanguageFlags.IsSet(LanguageFlags.TailRecursive); + lock (this.LockObject) { + var target = TargetRef.Evaluate(thread); + if (target is SpecialForm) { + _specialForm = target as SpecialForm; + _specialFormArgs = Arguments.ChildNodes.ToArray(); + this.Evaluate = EvaluateSpecialForm; + } else { + if (languageTailRecursive) { + var isTail = Flags.IsSet(AstNodeFlags.IsTail); + if (isTail) + this.Evaluate = EvaluateTail; + else + this.Evaluate = EvaluateWithTailCheck; + } else + this.Evaluate = EvaluateNoTail; + } + }//lock + } + + // Evaluation for special forms + private object EvaluateSpecialForm(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var result = _specialForm(thread, _specialFormArgs); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + + // Evaluation for non-tail languages + private object EvaluateNoTail(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var target = TargetRef.Evaluate(thread); + var iCall = target as ICallTarget; + if (iCall == null) + thread.ThrowScriptError(Resources.ErrVarIsNotCallable, _targetName); + var args = (object[])Arguments.Evaluate(thread); + object result = iCall.Call(thread, args); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + //Evaluation for tailed languages + private object EvaluateTail(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var target = TargetRef.Evaluate(thread); + var iCall = target as ICallTarget; + if (iCall == null) + thread.ThrowScriptError(Resources.ErrVarIsNotCallable, _targetName); + var args = (object[])Arguments.Evaluate(thread); + thread.Tail = iCall; + thread.TailArgs = args; + thread.CurrentNode = Parent; //standard epilog + return null; + } + + private object EvaluateWithTailCheck(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var target = TargetRef.Evaluate(thread); + var iCall = target as ICallTarget; + if (iCall == null) + thread.ThrowScriptError(Resources.ErrVarIsNotCallable, _targetName); + var args = (object[])Arguments.Evaluate(thread); + object result = null; + result = iCall.Call(thread, args); + //Note that after invoking tail we can get another tail. + // So we need to keep calling tails while they are there. + while (thread.Tail != null) { + var tail = thread.Tail; + var tailArgs = thread.TailArgs; + thread.Tail = null; + thread.TailArgs = null; + result = tail.Call(thread, tailArgs); + } + thread.CurrentNode = Parent; //standard epilog + return result; + } + + }//class + +}//namespace diff --git a/Irony.Interpreter/Ast/Functions/FunctionDefNode.cs b/Irony.Interpreter/Ast/Functions/FunctionDefNode.cs new file mode 100644 index 0000000..e089a59 --- /dev/null +++ b/Irony.Interpreter/Ast/Functions/FunctionDefNode.cs @@ -0,0 +1,56 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + //A node representing function definition + public class FunctionDefNode : AstNode { + public AstNode NameNode; + public LambdaNode Lambda; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + //child #0 is usually a keyword like "def" + var nodes = treeNode.GetMappedChildNodes(); + NameNode = AddChild("Name", nodes[1]); + Lambda = new LambdaNode(context, treeNode, nodes[2], nodes[3]); + Lambda.Parent = this; + AsString = ""; + //Lamda will set treeNode.AstNode to itself, we need to set it back to "this" here + treeNode.AstNode = this; // + } + + public override void Reset() { + DependentScopeInfo = null; + Lambda.Reset(); + base.Reset(); + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var closure = Lambda.Evaluate(thread); //returns closure + NameNode.SetValue(thread, closure); + thread.CurrentNode = Parent; //standard epilog + return closure; + } + + }//class + +}//namespace diff --git a/Irony.Interpreter/Ast/Functions/LambdaNode.cs b/Irony.Interpreter/Ast/Functions/LambdaNode.cs new file mode 100644 index 0000000..2a83922 --- /dev/null +++ b/Irony.Interpreter/Ast/Functions/LambdaNode.cs @@ -0,0 +1,96 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + //A node representing function definition + public class LambdaNode : AstNode { + public AstNode Parameters; + public AstNode Body; + + public LambdaNode() { } + + //Used by FunctionDefNode + public LambdaNode(AstContext context, ParseTreeNode node, ParseTreeNode parameters, ParseTreeNode body) { + InitImpl(context, node, parameters, body); + } + + public override void Init(AstContext context, ParseTreeNode parseNode) { + var mappedNodes = parseNode.GetMappedChildNodes(); + InitImpl(context, parseNode, mappedNodes[0], mappedNodes[1]); + } + + private void InitImpl(AstContext context, ParseTreeNode parseNode, ParseTreeNode parametersNode, ParseTreeNode bodyNode) { + base.Init(context, parseNode); + Parameters = AddChild("Parameters", parametersNode); + Body = AddChild("Body", bodyNode); + AsString = "Lambda[" + Parameters.ChildNodes.Count + "]"; + Body.SetIsTail(); //this will be propagated to the last statement + } + + public override void Reset() { + DependentScopeInfo = null; + base.Reset(); + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + lock (LockObject) { + if (DependentScopeInfo == null) { + var langCaseSensitive = thread.App.Language.Grammar.CaseSensitive; + DependentScopeInfo = new ScopeInfo(this, langCaseSensitive); + } + // In the first evaluation the parameter list will add parameter's SlotInfo objects to Scope.ScopeInfo + thread.PushScope(DependentScopeInfo, null); + Parameters.Evaluate(thread); + thread.PopScope(); + //Set Evaluate method and invoke it later + this.Evaluate = EvaluateAfter; + } + var result = Evaluate(thread); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + private object EvaluateAfter(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var closure = new Closure(thread.CurrentScope, this); + thread.CurrentNode = Parent; //standard epilog + return closure; + } + + public object Call(Scope creatorScope, ScriptThread thread, object[] parameters) { + var save = thread.CurrentNode; //prolog, not standard - the caller is NOT target node's parent + thread.CurrentNode = this; + thread.PushClosureScope(DependentScopeInfo, creatorScope, parameters); + Parameters.Evaluate(thread); // pre-process parameters + var result = Body.Evaluate(thread); + thread.PopScope(); + thread.CurrentNode = save; //epilog, restoring caller + return result; + } + + + public override void SetIsTail() { + //ignore this call, do not mark this node as tail, it is meaningless + } + }//class + +}//namespace diff --git a/Irony.Interpreter/Ast/Functions/ParamListNode.cs b/Irony.Interpreter/Ast/Functions/ParamListNode.cs new file mode 100644 index 0000000..ed8290e --- /dev/null +++ b/Irony.Interpreter/Ast/Functions/ParamListNode.cs @@ -0,0 +1,53 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + public class ParamListNode : AstNode { + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + foreach (var child in treeNode.ChildNodes) + AddChild(NodeUseType.Parameter, "param", child); + AsString = "param_list[" + ChildNodes.Count + "]"; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + // Is called once, at first evaluation of FunctionDefNode + // Creates parameter slots + foreach (var child in this.ChildNodes) { + var idNode = child as IdentifierNode; + if (idNode != null) { + thread.CurrentScope.Info.AddSlot(idNode.Symbol, SlotType.Parameter); + } + } + this.Evaluate = EvaluateAfter; + thread.CurrentNode = Parent; //standard epilog + return null; + }//method + + // TODO: implement handling list/dict parameter tails (Scheme, Python, etc) + private object EvaluateAfter(ScriptThread thread) { + return null; + } + }//class + +}//namespace diff --git a/Irony.Interpreter/Ast/PrimitiveNodes/IdentifierNode.cs b/Irony.Interpreter/Ast/PrimitiveNodes/IdentifierNode.cs new file mode 100644 index 0000000..72399be --- /dev/null +++ b/Irony.Interpreter/Ast/PrimitiveNodes/IdentifierNode.cs @@ -0,0 +1,56 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; + +using Irony.Parsing; +using Irony.Ast; + +namespace Irony.Interpreter.Ast { + + public class IdentifierNode : AstNode { + public string Symbol; + private Binding _accessor; + + public IdentifierNode() { } + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + Symbol = treeNode.Token.ValueString; + AsString = Symbol; + } + + //Executed only once, on the first call + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + _accessor = thread.Bind(Symbol, BindingRequestFlags.Read); + this.Evaluate = _accessor.GetValueRef; // Optimization - directly set method ref to accessor's method. EvaluateReader; + var result = this.Evaluate(thread); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + public override void DoSetValue(ScriptThread thread, object value) { + thread.CurrentNode = this; //standard prolog + if (_accessor == null) { + _accessor = thread.Bind(Symbol, BindingRequestFlags.Write | BindingRequestFlags.ExistingOrNew); + } + _accessor.SetValueRef(thread, value); + thread.CurrentNode = Parent; //standard epilog + } + + }//class +}//namespace diff --git a/Irony.Interpreter/Ast/PrimitiveNodes/LiteralValueNode.cs b/Irony.Interpreter/Ast/PrimitiveNodes/LiteralValueNode.cs new file mode 100644 index 0000000..36efb57 --- /dev/null +++ b/Irony.Interpreter/Ast/PrimitiveNodes/LiteralValueNode.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; +using Irony.Interpreter; + +namespace Irony.Interpreter.Ast { + public class LiteralValueNode : AstNode { + public object Value; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + Value = treeNode.Token.Value; + AsString = Value == null ? "null" : Value.ToString(); + if (Value is string) + AsString = "\"" + AsString + "\""; + } + + protected override object DoEvaluate(ScriptThread thread) { + return Value; + } + + public override bool IsConstant() { + return true; + } + }//class +} diff --git a/Irony.Interpreter/Ast/PrimitiveNodes/StringTemplateNode.cs b/Irony.Interpreter/Ast/PrimitiveNodes/StringTemplateNode.cs new file mode 100644 index 0000000..0109de6 --- /dev/null +++ b/Irony.Interpreter/Ast/PrimitiveNodes/StringTemplateNode.cs @@ -0,0 +1,166 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + // Implements Ruby-like active strings with embedded expressions + + /* Example of use: + + //String literal with embedded expressions ------------------------------------------------------------------ + var stringLit = new StringLiteral("string", "\"", StringOptions.AllowsAllEscapes | StringOptions.IsTemplate); + stringLit.AstNodeType = typeof(StringTemplateNode); + var Expr = new NonTerminal("Expr"); + var templateSettings = new StringTemplateSettings(); //by default set to Ruby-style settings + templateSettings.ExpressionRoot = Expr; //this defines how to evaluate expressions inside template + this.SnippetRoots.Add(Expr); + stringLit.AstNodeConfig = templateSettings; + + //define Expr as an expression non-terminal in your grammar + + */ + + + public class StringTemplateNode : AstNode { + #region embedded classes + enum SegmentType { + Text, + Expression + } + class TemplateSegment { + public SegmentType Type; + public string Text; + public AstNode ExpressionNode; + public int Position; //Position in raw text of the token for error reporting + public TemplateSegment(string text, AstNode node, int position) { + Type = node == null? SegmentType.Text : SegmentType.Expression; + Text = text; + ExpressionNode = node; + Position = position; + } + } + class SegmentList : List { } + #endregion + + string _template; + string _tokenText; //used for locating error + StringTemplateSettings _templateSettings; //copied from Terminal.AstNodeConfig + SegmentList _segments = new SegmentList(); + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + _template = treeNode.Token.ValueString; + _tokenText = treeNode.Token.Text; + _templateSettings = treeNode.Term.AstConfig.Data as StringTemplateSettings; + ParseSegments(context); + AsString = "\"" + _template + "\" (templated string)"; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var value = BuildString(thread); + thread.CurrentNode = Parent; //standard epilog + return value; + } + + private void ParseSegments(AstContext context) { + var exprParser = new Parser(context.Language, _templateSettings.ExpressionRoot); + // As we go along the "value text" (that has all escapes done), we track the position in raw token text in the variable exprPosInTokenText. + // This position is position in original text in source code, including original escaping sequences and open/close quotes. + // It will be passed to segment constructor, and maybe used later to compute the exact position of runtime error when it occurs. + int currentPos = 0, exprPosInTokenText = 0; + while(true) { + var startTagPos = _template.IndexOf(_templateSettings.StartTag, currentPos); + if (startTagPos < 0) startTagPos = _template.Length; + var text = _template.Substring(currentPos, startTagPos - currentPos); + if (!string.IsNullOrEmpty(text)) + _segments.Add(new TemplateSegment(text, null, 0)); //for text segments position is not used + if (startTagPos >= _template.Length) + break; //from while + //We have a real start tag, grab the expression + currentPos = startTagPos + _templateSettings.StartTag.Length; + var endTagPos = _template.IndexOf(_templateSettings.EndTag, currentPos); + if (endTagPos < 0) { + //"No ending tag '{0}' found in embedded expression." + context.AddMessage(ErrorLevel.Error, this.Location, Resources.ErrNoEndTagInEmbExpr, _templateSettings.EndTag); + return; + } + var exprText = _template.Substring(currentPos, endTagPos - currentPos); + if(!string.IsNullOrEmpty(exprText)) { + //parse the expression + //_expressionParser.context.Reset(); + + var exprTree = exprParser.Parse(exprText); + if(exprTree.HasErrors()) { + //we use original search in token text instead of currentPos in template to avoid distortions caused by opening quote and escaped sequences + var baseLocation = this.Location + _tokenText.IndexOf(exprText); + CopyMessages(exprTree.ParserMessages, context.Messages, baseLocation, Resources.ErrInvalidEmbeddedPrefix); + return; + } + //add the expression segment + exprPosInTokenText = _tokenText.IndexOf(_templateSettings.StartTag, exprPosInTokenText) + _templateSettings.StartTag.Length; + var segmNode = exprTree.Root.AstNode as AstNode; + segmNode.Parent = this; //important to attach the segm node to current Module + _segments.Add(new TemplateSegment(null, segmNode, exprPosInTokenText)); + //advance position beyond the expression + exprPosInTokenText += exprText.Length + _templateSettings.EndTag.Length; + + }//if + currentPos = endTagPos + _templateSettings.EndTag.Length; + }//while + } + + private void CopyMessages(LogMessageList fromList, LogMessageList toList, SourceLocation baseLocation, string messagePrefix) { + foreach (var other in fromList) + toList.Add(new LogMessage(other.Level, baseLocation + other.Location, messagePrefix + other.Message, other.ParserState)); + }// + + + private object BuildString(ScriptThread thread) { + string[] values = new string[_segments.Count]; + for(int i = 0; i < _segments.Count; i++) { + var segment = _segments[i]; + switch(segment.Type) { + case SegmentType.Text: + values[i] = segment.Text; + break; + case SegmentType.Expression: + values[i] = EvaluateExpression(thread, segment); + break; + }//else + }//for i + var result = string.Join(string.Empty, values); + return result; + }//method + + private string EvaluateExpression(ScriptThread thread, TemplateSegment segment) { + try { + var value = segment.ExpressionNode.Evaluate(thread); + return value == null ? string.Empty : value.ToString(); + } catch { + //We need to catch here and set current node; ExpressionNode may have reset it, and location would be wrong + //TODO: fix this - set error location to exact location inside string. + thread.CurrentNode = this; + throw; + } + + } + }//class +} diff --git a/Irony.Interpreter/Ast/SpecialNodes/EmptyStatementNode.cs b/Irony.Interpreter/Ast/SpecialNodes/EmptyStatementNode.cs new file mode 100644 index 0000000..300f3a3 --- /dev/null +++ b/Irony.Interpreter/Ast/SpecialNodes/EmptyStatementNode.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; +using Irony.Interpreter; + +namespace Irony.Interpreter.Ast { + //A statement that does nothing, like "pass" command in Python. + public class EmptyStatementNode : AstNode { + + + }//class +} diff --git a/Irony.Interpreter/Ast/SpecialNodes/NotSupportedNode.cs b/Irony.Interpreter/Ast/SpecialNodes/NotSupportedNode.cs new file mode 100644 index 0000000..ff162f8 --- /dev/null +++ b/Irony.Interpreter/Ast/SpecialNodes/NotSupportedNode.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + //A substitute node to use on constructs that are not yet supported by language implementation. + // The script would compile Ok but on attempt to evaluate the node would throw a runtime exception + public class NotSupportedNode : AstNode { + string Name; + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + Name = treeNode.Term.ToString(); + AsString = Name + " (not supported)"; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + thread.ThrowScriptError(Resources.ErrConstructNotSupported, Name); + return null; //never happens + } + + }//class +} diff --git a/Irony.Interpreter/Ast/SpecialNodes/NullNode.cs b/Irony.Interpreter/Ast/SpecialNodes/NullNode.cs new file mode 100644 index 0000000..7585c28 --- /dev/null +++ b/Irony.Interpreter/Ast/SpecialNodes/NullNode.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; +using Irony.Interpreter; + +namespace Irony.Interpreter.Ast { + //A stub to use when AST node was not created (type not specified on NonTerminal, or error on creation) + // The purpose of the stub is to throw a meaningful message when interpreter tries to evaluate null node. + public class NullNode : AstNode { + + public NullNode(BnfTerm term) { + this.Term = term; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + thread.ThrowScriptError(Resources.ErrNullNodeEval, this.Term); + return null; //never happens + } + }//class +} diff --git a/Irony.Interpreter/Ast/Statements/AssignmentNode.cs b/Irony.Interpreter/Ast/Statements/AssignmentNode.cs new file mode 100644 index 0000000..7c59ec5 --- /dev/null +++ b/Irony.Interpreter/Ast/Statements/AssignmentNode.cs @@ -0,0 +1,109 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + public class AssignmentNode : AstNode { + public AstNode Target; + public string AssignmentOp; + public bool IsAugmented; // true if it is augmented operation like "+=" + public ExpressionType BinaryExpressionType; + public AstNode Expression; + private OperatorImplementation _lastUsed; + private int _failureCount; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + Target = AddChild(NodeUseType.ValueWrite, "To", nodes[0]); + //Get Op and baseOp if it is combined assignment + AssignmentOp = nodes[1].FindTokenAndGetText(); + if (string.IsNullOrEmpty(AssignmentOp)) + AssignmentOp = "="; + BinaryExpressionType = CustomExpressionTypes.NotAnExpression; + //There maybe an "=" sign in the middle, or not - if it is marked as punctuation; so we just take the last node in child list + Expression = AddChild(NodeUseType.ValueRead, "Expr", nodes[nodes.Count - 1]); + AsString = AssignmentOp + " (assignment)"; + // TODO: this is not always correct: in Pascal the assignment operator is :=. + IsAugmented = AssignmentOp.Length > 1; + if (IsAugmented) { + + var ictxt = context as InterpreterAstContext; + base.ExpressionType = ictxt.OperatorHandler.GetOperatorExpressionType(AssignmentOp); + BinaryExpressionType = ictxt.OperatorHandler.GetBinaryOperatorForAugmented(this.ExpressionType); + Target.UseType = NodeUseType.ValueReadWrite; + } + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + if (IsAugmented) + Evaluate = EvaluateAugmentedFast; + else + Evaluate = EvaluateSimple; //non-augmented + //call self-evaluate again, now to call real methods + var result = this.Evaluate(thread); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + + private object EvaluateSimple(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var value = Expression.Evaluate(thread); + Target.SetValue(thread, value); + thread.CurrentNode = Parent; //standard epilog + return value; + } + + private object EvaluateAugmentedFast(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var value = Target.Evaluate(thread); + var exprValue = Expression.Evaluate(thread); + object result = null; + if (_lastUsed != null) { + try { + result = _lastUsed.EvaluateBinary(value, exprValue); + } catch { + _failureCount++; + // if failed 3 times, change to method without direct try + if (_failureCount > 3) + Evaluate = EvaluateAugmented; + } //catch + }// if _lastUsed + if (result == null) + result = thread.Runtime.ExecuteBinaryOperator(BinaryExpressionType, value, exprValue, ref _lastUsed); + Target.SetValue(thread, result); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + private object EvaluateAugmented(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var value = Target.Evaluate(thread); + var exprValue = Expression.Evaluate(thread); + var result = thread.Runtime.ExecuteBinaryOperator(BinaryExpressionType, value, exprValue, ref _lastUsed); + Target.SetValue(thread, result); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + + } +} diff --git a/Irony.Interpreter/Ast/Statements/SpecialFormNode.cs b/Irony.Interpreter/Ast/Statements/SpecialFormNode.cs new file mode 100644 index 0000000..e5527d9 --- /dev/null +++ b/Irony.Interpreter/Ast/Statements/SpecialFormNode.cs @@ -0,0 +1,134 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + //A node representing special form - a statement handled by a SpecialForm method provided by Runtime. + public class SpecialFormNode : AstNode { + string _formName; + SpecialForm _specialForm; + AstNode[] _specialFormArgs; + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + TargetRef = AddChild("Target", nodes[0]); + TargetRef.UseType = NodeUseType.CallTarget; + _targetName = nodes[0].FindTokenAndGetText(); + Arguments = AddChild("Args", nodes[1]); + AsString = "Call " + _targetName; + context.Language. + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + SetupEvaluateMethod(thread); + var result = Evaluate(thread); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + private void SetupEvaluateMethod(ScriptThread thread) { + var languageTailRecursive = thread.Runtime.Language.Grammar.LanguageFlags.IsSet(LanguageFlags.TailRecursive); + lock (this.LockObject) { + _specialForm = null; + var bnd = thread.Bind(_formName, BindingRequestFlags.Invoke); + if (bnd != null) + _specialForm = bnd.GetValueRef(thread) as SpecialForm; + IBindingSource src; + var target = thread.Runtime.BuiltIns.TryGetValue(_formName, out src); + if (target is SpecialForm) { + _specialForm = target as SpecialForm; + _specialFormArgs = Arguments.ChildNodes.ToArray(); + this.Evaluate = EvaluateSpecialForm; + } else { + if (languageTailRecursive) { + var isTail = Flags.IsSet(AstNodeFlags.IsTail); + if (isTail) + this.Evaluate = EvaluateTail; + else + this.Evaluate = EvaluateWithTailCheck; + } else + this.Evaluate = EvaluateNoTail; + } + }//lock + } + + // Evaluation for special forms + private object EvaluateSpecialForm(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var result = _specialForm(thread, _specialFormArgs); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + + // Evaluation for non-tail languages + private object EvaluateNoTail(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var target = TargetRef.Evaluate(thread); + var iCall = target as ICallTarget; + if (iCall == null) + thread.ThrowScriptError(Resources.ErrVarIsNotCallable, _targetName); + var args = (object[])Arguments.Evaluate(thread); + object result = iCall.Call(thread, args); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + //Evaluation for tailed languages + private object EvaluateTail(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var target = TargetRef.Evaluate(thread); + var iCall = target as ICallTarget; + if (iCall == null) + thread.ThrowScriptError(Resources.ErrVarIsNotCallable, _targetName); + var args = (object[])Arguments.Evaluate(thread); + thread.Tail = iCall; + thread.TailArgs = args; + thread.CurrentNode = Parent; //standard epilog + return null; + } + + private object EvaluateWithTailCheck(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + var target = TargetRef.Evaluate(thread); + var iCall = target as ICallTarget; + if (iCall == null) + thread.ThrowScriptError(Resources.ErrVarIsNotCallable, _targetName); + var args = (object[])Arguments.Evaluate(thread); + object result = null; + result = iCall.Call(thread, args); + //Note that after invoking tail we can get another tail. + // So we need to keep calling tails while they are there. + while (thread.Tail != null) { + var tail = thread.Tail; + var tailArgs = thread.TailArgs; + thread.Tail = null; + thread.TailArgs = null; + result = tail.Call(thread, tailArgs); + } + thread.CurrentNode = Parent; //standard epilog + return result; + } + + }//class + +}//namespace diff --git a/Irony.Interpreter/Ast/Statements/StatementListNode.cs b/Irony.Interpreter/Ast/Statements/StatementListNode.cs new file mode 100644 index 0000000..2de0fff --- /dev/null +++ b/Irony.Interpreter/Ast/Statements/StatementListNode.cs @@ -0,0 +1,93 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; + +namespace Irony.Interpreter.Ast { + + public class StatementListNode : AstNode { + AstNode _singleChild; //stores a single child when child count == 1, for fast access + + public override void Init(AstContext context, ParseTreeNode treeNode) { + base.Init(context, treeNode); + var nodes = treeNode.GetMappedChildNodes(); + foreach (var child in nodes) { + //don't add if it is null; it can happen that "statement" is a comment line and statement's node is null. + // So to make life easier for language creator, we just skip if it is null + if (child.AstNode != null) + AddChild(string.Empty, child); + } + AsString = "Statement List"; + if (ChildNodes.Count == 0) { + AsString += " (Empty)"; + } else + ChildNodes[ChildNodes.Count - 1].Flags |= AstNodeFlags.IsTail; + } + + protected override object DoEvaluate(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + lock (LockObject) { + switch (ChildNodes.Count) { + case 0: + Evaluate = EvaluateEmpty; + break; + case 1: + _singleChild = ChildNodes[0]; + Evaluate = EvaluateOne; + break; + default: + Evaluate = EvaluateMultiple; + break; + }//switch + }//lock + var result = Evaluate(thread); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + private object EvaluateEmpty(ScriptThread thread) { + return null; + } + + private object EvaluateOne(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + object result = _singleChild.Evaluate(thread); + thread.CurrentNode = Parent; //standard epilog + return result; + } + + private object EvaluateMultiple(ScriptThread thread) { + thread.CurrentNode = this; //standard prolog + object result = null; + for (int i=0; i< ChildNodes.Count; i++) { + result = ChildNodes[i].Evaluate(thread); + } + thread.CurrentNode = Parent; //standard epilog + return result; //return result of last statement + } + + public override void SetIsTail() { + base.SetIsTail(); + if (ChildNodes.Count > 0) + ChildNodes[ChildNodes.Count - 1].SetIsTail(); + } + + + }//class + +}//namespace diff --git a/Irony.Interpreter/Bindings/Binding.cs b/Irony.Interpreter/Bindings/Binding.cs new file mode 100644 index 0000000..601aee3 --- /dev/null +++ b/Irony.Interpreter/Bindings/Binding.cs @@ -0,0 +1,58 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + + // Binding is a link between a variable in the script (for ex, IdentifierNode) and a value storage - + // a slot in local or module-level Scope. Binding to internal variables is supported by SlotBinding class. + // Alternatively a symbol can be bound to external CLR entity in imported namespace - class, function, property, etc. + // Binding is produced by Runtime.Bind method and allows read/write operations through GetValueRef and SetValueRef methods. + public class Binding { + public readonly BindingTargetInfo TargetInfo; + public EvaluateMethod GetValueRef; // ref to Getter method implementation + public ValueSetterMethod SetValueRef; // ref to Setter method implementation + public bool IsConstant { get; protected set; } + public Binding(BindingTargetInfo targetInfo) { + TargetInfo = targetInfo; + } + public Binding(string symbol, BindingTargetType targetType) { + TargetInfo = new BindingTargetInfo(symbol, targetType); + } + public override string ToString() { + return "{Binding to + " + TargetInfo.ToString() + "}"; + } + }//class + + //Binding to a "fixed", constant value + public class ConstantBinding : Binding { + public object Target; + public ConstantBinding(object target, BindingTargetInfo targetInfo) : base(targetInfo) { + Target = target; + base.GetValueRef = GetValue; + IsConstant = true; + } + + public object GetValue(ScriptThread thread) { + return Target; + } + + } + +} diff --git a/Irony.Interpreter/Bindings/BindingRequest.cs b/Irony.Interpreter/Bindings/BindingRequest.cs new file mode 100644 index 0000000..94a1f36 --- /dev/null +++ b/Irony.Interpreter/Bindings/BindingRequest.cs @@ -0,0 +1,51 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + [Flags] + public enum BindingRequestFlags { + Read = 0x01, + Write = 0x02, + Invoke = 0x04, + ExistingOrNew = 0x10, + NewOnly = 0x20, // for new variable, for ex, in JavaScript "var x..." - introduces x as new variable + } + + //Binding request is a container for information about requested binding. Binding request goes from an Ast node to language runtime. + // For example, identifier node would request a binding for an identifier. + public class BindingRequest { + public ScriptThread Thread; + public AstNode FromNode; + public ModuleInfo FromModule; + public BindingRequestFlags Flags; + public string Symbol; + public ScopeInfo FromScopeInfo; + public bool IgnoreCase; + public BindingRequest(ScriptThread thread, AstNode fromNode, string symbol, BindingRequestFlags flags) { + Thread = thread; + FromNode = fromNode; + FromModule = thread.App.DataMap.GetModule(fromNode.ModuleNode); + Symbol = symbol; + Flags = flags; + FromScopeInfo = thread.CurrentScope.Info; + IgnoreCase = !thread.Runtime.Language.Grammar.CaseSensitive; + } + } + +} diff --git a/Irony.Interpreter/Bindings/BindingTargetInfo.cs b/Irony.Interpreter/Bindings/BindingTargetInfo.cs new file mode 100644 index 0000000..889dd67 --- /dev/null +++ b/Irony.Interpreter/Bindings/BindingTargetInfo.cs @@ -0,0 +1,44 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter { + + public enum BindingTargetType { + Slot, + BuiltInObject, + SpecialForm, + ClrInterop, + Custom, // any special non-standard type for specific language + } + + + public class BindingTargetInfo { + public readonly string Symbol; + public readonly BindingTargetType Type; + public BindingTargetInfo(string symbol, BindingTargetType type) { + Symbol = symbol; + Type = type; + } + + public override string ToString() { + return Symbol + "/" + Type.ToString(); + } + + }//class + + +} diff --git a/Irony.Interpreter/Bindings/BuiltInObjectBinding.cs b/Irony.Interpreter/Bindings/BuiltInObjectBinding.cs new file mode 100644 index 0000000..6f0c715 --- /dev/null +++ b/Irony.Interpreter/Bindings/BuiltInObjectBinding.cs @@ -0,0 +1,79 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + // A general delegate representing a built-in method implementation. + public delegate object BuiltInMethod(ScriptThread thread, object[] args); + + //A wrapper to convert BuiltInMethod delegate (referencing some custom method in LanguageRuntime) into an ICallTarget instance (expected by FunctionCallNode) + public class BuiltInCallTarget : ICallTarget { + public string Name; + public readonly BuiltInMethod Method; + public readonly int MinParamCount, MaxParamCount; + public string[] ParameterNames; //Just for information purpose + public BuiltInCallTarget(BuiltInMethod method, string name, int minParamCount = 0, int maxParamCount = 0, string parameterNames = null) { + Method = method; + Name = name; + MinParamCount = minParamCount; + MaxParamCount = Math.Max(MinParamCount, maxParamCount); + if (!string.IsNullOrEmpty(parameterNames)) + ParameterNames = parameterNames.Split(','); + } + + #region ICallTarget Members + public object Call(ScriptThread thread, object[] parameters) { + return Method(thread, parameters); + } + #endregion + } + + // The class contains information about built-in function. It has double purpose. + // First, it is used as a BindingTargetInfo instance (meta-data) for a binding to a built-in function. + // Second, we use it as a reference to a custom built-in method that we store in LanguageRuntime.BuiltIns table. + // For this, we make it implement IBindingSource - we can add it to BuiltIns table of LanguageRuntime, which is a table of IBindingSource instances. + // Being IBindingSource, it can produce a binding object to the target method - singleton in fact; + // the same binding object is used for all calls to the method from all function-call AST nodes. + public class BuiltInCallableTargetInfo : BindingTargetInfo, IBindingSource { + public Binding BindingInstance; //A singleton binding instance; we share it for all AST nodes (function call nodes) that call the method. + + public BuiltInCallableTargetInfo(BuiltInMethod method, string methodName, int minParamCount = 0, int maxParamCount = 0, string parameterNames = null) : + this(new BuiltInCallTarget(method, methodName, minParamCount, maxParamCount, parameterNames)) { + } + public BuiltInCallableTargetInfo(BuiltInCallTarget target) : base(target.Name, BindingTargetType.BuiltInObject) { + BindingInstance = new ConstantBinding(target, this); + } + + //Implement IBindingSource.Bind + public Binding Bind(BindingRequest request) { + return BindingInstance; + } + + }//class + + // Method for adding methods to BuiltIns table in Runtime + public static partial class BindingSourceTableExtensions { + public static BindingTargetInfo AddMethod(this BindingSourceTable targets, BuiltInMethod method, string methodName, + int minParamCount = 0, int maxParamCount = 0, string parameterNames = null) { + var callTarget = new BuiltInCallTarget(method, methodName, minParamCount, maxParamCount, parameterNames); + var targetInfo = new BuiltInCallableTargetInfo(callTarget); + targets.Add(methodName, targetInfo); + return targetInfo; + } + } + +}//namespace diff --git a/Irony.Interpreter/Bindings/ClrInteropBindings.cs b/Irony.Interpreter/Bindings/ClrInteropBindings.cs new file mode 100644 index 0000000..4fabb86 --- /dev/null +++ b/Irony.Interpreter/Bindings/ClrInteropBindings.cs @@ -0,0 +1,168 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + //Unfinished, work in progress, file disabled for now + + public enum ClrTargetType { + Namespace, + Type, + Method, + Property, + Field, + } + + public class ClrInteropBindingTargetInfo : BindingTargetInfo, IBindingSource { + public ClrTargetType TargetSubType; + + public ClrInteropBindingTargetInfo(string symbol, ClrTargetType targetSubType) : base(symbol, BindingTargetType.ClrInterop) { + TargetSubType = targetSubType; + } + + public virtual Binding Bind(BindingRequest request) { + throw new NotImplementedException(); + } + }//class + + public class ClrNamespaceBindingTargetInfo : ClrInteropBindingTargetInfo { + ConstantBinding _binding; + public ClrNamespaceBindingTargetInfo(string ns) : base(ns, ClrTargetType.Namespace) { + _binding = new ConstantBinding(ns, this); + } + public override Binding Bind(BindingRequest request) { + return _binding; + } + } + + public class ClrTypeBindingTargetInfo : ClrInteropBindingTargetInfo { + ConstantBinding _binding; + public ClrTypeBindingTargetInfo(Type type) : base(type.Name, ClrTargetType.Type) { + _binding = new ConstantBinding(type, this); + } + public override Binding Bind(BindingRequest request) { + return _binding; + } + } + + public class ClrMethodBindingTargetInfo : ClrInteropBindingTargetInfo, ICallTarget { //The object works as ICallTarget itself + public object Instance; + public Type DeclaringType; + BindingFlags _invokeFlags; + Binding _binding; + + public ClrMethodBindingTargetInfo(Type declaringType, string methodName, object instance = null) : base(methodName, ClrTargetType.Method) { + DeclaringType = declaringType; + Instance = instance; + _invokeFlags = BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.NonPublic; + if (Instance == null) + _invokeFlags |= BindingFlags.Static; + else + _invokeFlags |= BindingFlags.Instance; + _binding = new ConstantBinding(target: this as ICallTarget, targetInfo: this); + //The object works as CallTarget itself; the "as" conversion is not needed in fact, we do it just to underline the role + } + + public override Binding Bind(BindingRequest request) { + return _binding; + } + + #region ICalllable.Call implementation + public object Call(ScriptThread thread, object[] args) { + // TODO: fix this. Currently doing it slow but easy way, through reflection + if (args != null && args.Length == 0) + args = null; + var result = DeclaringType.InvokeMember(base.Symbol, _invokeFlags, null, Instance, args); + return result; + } + #endregion + } + + public class ClrPropertyBindingTargetInfo : ClrInteropBindingTargetInfo { + public object Instance; + public PropertyInfo Property; + Binding _binding; + + public ClrPropertyBindingTargetInfo(PropertyInfo property, object instance) : base(property.Name, ClrTargetType.Property) { + Property = property; + Instance = instance; + _binding = new Binding(this); + _binding.GetValueRef = GetPropertyValue; + _binding.SetValueRef = SetPropertyValue; + } + public override Binding Bind(BindingRequest request) { + return _binding; + } + private object GetPropertyValue(ScriptThread thread) { + var result = Property.GetValue(Instance, null); + return result; + } + private void SetPropertyValue(ScriptThread thread, object value) { + Property.SetValue(Instance, value, null); + } + } + + public class ClrFieldBindingTargetInfo : ClrInteropBindingTargetInfo { + public object Instance; + public FieldInfo Field; + Binding _binding; + + public ClrFieldBindingTargetInfo(FieldInfo field, object instance) : base(field.Name, ClrTargetType.Field) { + Field = field; + Instance = instance; + _binding = new Binding(this); + _binding.GetValueRef = GetPropertyValue; + _binding.SetValueRef = SetPropertyValue; + } + public override Binding Bind(BindingRequest request) { + return _binding; + } + private object GetPropertyValue(ScriptThread thread) { + var result = Field.GetValue(Instance); + return result; + } + private void SetPropertyValue(ScriptThread thread, object value) { + Field.SetValue(Instance, value); + } + } + + // Method for adding methods to BuiltIns table in Runtime + public static partial class BindingSourceTableExtensions { + public static void ImportStaticMembers(this BindingSourceTable targets, Type fromType) { + var members = fromType.GetMembers(BindingFlags.Public | BindingFlags.Static); + foreach (var member in members) { + if (targets.ContainsKey(member.Name)) continue; //do not import overloaded methods several times + switch (member.MemberType) { + case MemberTypes.Method: + targets.Add(member.Name, new ClrMethodBindingTargetInfo(fromType, member.Name)); + break; + case MemberTypes.Property: + targets.Add(member.Name, new ClrPropertyBindingTargetInfo(member as PropertyInfo, null)); + break; + case MemberTypes.Field: + targets.Add(member.Name, new ClrFieldBindingTargetInfo(member as FieldInfo, null)); + break; + }//switch + }//foreach + }//method + } + + + +} diff --git a/Irony.Interpreter/Bindings/IBindingSource.cs b/Irony.Interpreter/Bindings/IBindingSource.cs new file mode 100644 index 0000000..4d3e210 --- /dev/null +++ b/Irony.Interpreter/Bindings/IBindingSource.cs @@ -0,0 +1,44 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter { + + public interface IBindingSource { + Binding Bind(BindingRequest request); + } + + public class BindingSourceList : List { + } + + public class BindingSourceTable : Dictionary, IBindingSource { + public BindingSourceTable(bool caseSensitive) + : base(caseSensitive ? StringComparer.InvariantCulture : StringComparer.InvariantCultureIgnoreCase) { + } + //IBindingSource Members + public Binding Bind(BindingRequest request) { + IBindingSource target; + if (TryGetValue(request.Symbol, out target)) + return target.Bind(request); + return null; + } + }//class + + // This class will be used to define extensions for BindingSourceTable + public static partial class BindingSourceTableExtensions { + } + +} diff --git a/Irony.Interpreter/Bindings/ModuleExport.cs b/Irony.Interpreter/Bindings/ModuleExport.cs new file mode 100644 index 0000000..22444a2 --- /dev/null +++ b/Irony.Interpreter/Bindings/ModuleExport.cs @@ -0,0 +1,36 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Irony.Interpreter { + + // Module export, container for public, exported symbols from module + // Just a skeleton, to be completed + public class ModuleExport: IBindingSource { + public ModuleInfo Module; + public ModuleExport(ModuleInfo module) { + Module = module; + } + + public Binding Bind(BindingRequest request) { + return null; + } + } + + + +} diff --git a/Irony.Interpreter/Bindings/SlotBinding.cs b/Irony.Interpreter/Bindings/SlotBinding.cs new file mode 100644 index 0000000..c5f0977 --- /dev/null +++ b/Irony.Interpreter/Bindings/SlotBinding.cs @@ -0,0 +1,202 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + // Implements fast access to a variable (local/global var or parameter) in local scope or in any enclosing scope + // Important: the following code is very sensitive to even tiny changes - do not know exactly particular reasons. + public sealed class SlotBinding : Binding { + public SlotInfo Slot; + public ScopeInfo FromScope; + public int SlotIndex; + public int StaticScopeIndex; + public AstNode FromNode; + + public SlotBinding(SlotInfo slot, AstNode fromNode, ScopeInfo fromScope) : base(slot.Name, BindingTargetType.Slot) { + Slot = slot; + FromNode = fromNode; + FromScope = fromScope; + SlotIndex = slot.Index; + StaticScopeIndex = Slot.ScopeInfo.StaticIndex; + SetupAccessorMethods(); + } + + private void SetupAccessorMethods() { + // Check module scope + if (Slot.ScopeInfo.StaticIndex >= 0) { + GetValueRef = FastGetStaticValue; + SetValueRef = SetStatic; + return; + } + var levelDiff = Slot.ScopeInfo.Level - FromScope.Level; + switch (levelDiff) { + case 0: // local scope + if (Slot.Type == SlotType.Value) { + base.GetValueRef = FastGetCurrentScopeValue; + base.SetValueRef = SetCurrentScopeValue; + } else { + base.GetValueRef = FastGetCurrentScopeParameter; + base.SetValueRef = SetCurrentScopeParameter; + } + return; + case 1: //direct parent + if (Slot.Type == SlotType.Value) { + base.GetValueRef = GetImmediateParentScopeValue; + base.SetValueRef = SetImmediateParentScopeValue; + } else { + base.GetValueRef = GetImmediateParentScopeParameter; + base.SetValueRef = SetImmediateParentScopeParameter; + } + return; + default: // some enclosing scope + if (Slot.Type == SlotType.Value) { + base.GetValueRef = GetParentScopeValue; + base.SetValueRef = SetParentScopeValue; + } else { + base.GetValueRef = GetParentScopeParameter; + base.SetValueRef = SetParentScopeParameter; + } + return; + } + } + + // Specific method implementations ======================================================================================================= + // Optimization: in most cases we go directly for Values array; if we fail, then we fallback to full method + // with proper exception handling. This fallback is expected to be extremely rare, so overall we have considerable perf gain + // Note that in we expect the methods to be used directly by identifier node (like: IdentifierNode.EvaluateRef = Binding.GetValueRef; } - + // to save a few processor cycles. Therefore, we need to provide a proper context (thread.CurrentNode) in case of exception. + // In all "full-method" implementations we set current node to FromNode, so exception correctly points + // to the owner Identifier node as a location of error. + + // Current scope + private object FastGetCurrentScopeValue(ScriptThread thread) { + try { + //optimization: we go directly for values array; if we fail, then we fallback to regular "proper" method. + return thread.CurrentScope.Values[SlotIndex]; + } catch { + return GetCurrentScopeValue(thread); + } + } + + private object GetCurrentScopeValue(ScriptThread thread) { + try { + return thread.CurrentScope.GetValue(SlotIndex); + } catch { thread.CurrentNode = FromNode; throw; } + } + + + private object FastGetCurrentScopeParameter(ScriptThread thread) { + //optimization: we go directly for parameters array; if we fail, then we fallback to regular "proper" method. + try { + return thread.CurrentScope.Parameters[SlotIndex]; + } catch { + return GetCurrentScopeParameter(thread); + } + } + private object GetCurrentScopeParameter(ScriptThread thread) { + try { + return thread.CurrentScope.GetParameter(SlotIndex); + } catch { thread.CurrentNode = FromNode; throw; } + } + + private void SetCurrentScopeValue(ScriptThread thread, object value) { + thread.CurrentScope.SetValue(SlotIndex, value); + } + + private void SetCurrentScopeParameter(ScriptThread thread, object value) { + thread.CurrentScope.SetParameter(SlotIndex, value); + } + + // Static scope (module-level variables) + private object FastGetStaticValue(ScriptThread thread) { + try { + return thread.App.StaticScopes[StaticScopeIndex].Values[SlotIndex]; + } catch { + return GetStaticValue(thread); + } + } + private object GetStaticValue(ScriptThread thread) { + try { + return thread.App.StaticScopes[StaticScopeIndex].GetValue(SlotIndex); + } catch { thread.CurrentNode = FromNode; throw; } + } + + + private void SetStatic(ScriptThread thread, object value) { + thread.App.StaticScopes[StaticScopeIndex].SetValue(SlotIndex, value); + } + + // Direct parent + private object GetImmediateParentScopeValue(ScriptThread thread) { + try { + return thread.CurrentScope.Parent.Values[SlotIndex]; + } catch { } + //full method + try { + return thread.CurrentScope.Parent.GetValue(SlotIndex); + } catch { thread.CurrentNode = FromNode; throw; } + } + + private object GetImmediateParentScopeParameter(ScriptThread thread) { + try { + return thread.CurrentScope.Parent.Parameters[SlotIndex]; + } catch { } + //full method + try { + return thread.CurrentScope.Parent.GetParameter(SlotIndex); + } catch { thread.CurrentNode = FromNode; throw; } + } + + private void SetImmediateParentScopeValue(ScriptThread thread, object value) { + thread.CurrentScope.Parent.SetValue(SlotIndex, value); + } + + private void SetImmediateParentScopeParameter(ScriptThread thread, object value) { + thread.CurrentScope.Parent.SetParameter(SlotIndex, value); + } + + // Generic case + private object GetParentScopeValue(ScriptThread thread) { + var targetScope = GetTargetScope(thread); + return targetScope.GetValue(SlotIndex); + } + private object GetParentScopeParameter(ScriptThread thread) { + var targetScope = GetTargetScope(thread); + return targetScope.GetParameter(SlotIndex); + } + private void SetParentScopeValue(ScriptThread thread, object value) { + var targetScope = GetTargetScope(thread); + targetScope.SetValue(SlotIndex, value); + } + private void SetParentScopeParameter(ScriptThread thread, object value) { + var targetScope = GetTargetScope(thread); + targetScope.SetParameter(SlotIndex, value); + } + private Scope GetTargetScope(ScriptThread thread) { + var targetLevel = Slot.ScopeInfo.Level; + var scope = thread.CurrentScope.Parent; + while (scope.Info.Level > targetLevel) + scope = scope.Parent; + return scope; + } + + + }//class SlotReader + +} + diff --git a/Irony.Interpreter/Bindings/SpecialFormBinding.cs b/Irony.Interpreter/Bindings/SpecialFormBinding.cs new file mode 100644 index 0000000..7c6fd71 --- /dev/null +++ b/Irony.Interpreter/Bindings/SpecialFormBinding.cs @@ -0,0 +1,57 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + + public class SpecialFormBindingInfo : BindingTargetInfo, IBindingSource { + public readonly ConstantBinding Binding; + public readonly int MinChildCount, MaxChildCount; + public string[] ChildRoles; + public SpecialFormBindingInfo(string symbol, SpecialForm form, int minChildCount = 0, int maxChildCount = 0, string childRoles = null) + : base(symbol, BindingTargetType.SpecialForm) { + Binding = new ConstantBinding(form, this); + MinChildCount = minChildCount; + MaxChildCount = Math.Max(minChildCount, maxChildCount); //if maxParamCount=0 then set it equal to minParamCount + if (!string.IsNullOrEmpty(childRoles)) { + ChildRoles = childRoles.Split(','); + //TODO: add check that paramNames array is in accord with min/max param counts + } + } + + #region IBindingSource Members + + public Binding Bind(BindingRequest request) { + return Binding; + } + + #endregion + }//class + + public static partial class BindingSourceTableExtensions { + //Method for adding methods to BuiltIns table in Runtime + public static BindingTargetInfo AddSpecialForm(this BindingSourceTable targets, SpecialForm form, string formName, + int minChildCount = 0, int maxChildCount = 0, string parameterNames = null) { + var formInfo = new SpecialFormBindingInfo(formName, form, minChildCount, maxChildCount, parameterNames); + targets.Add(formName, formInfo); + return formInfo; + } + + } + +}//namespace diff --git a/Irony.Interpreter/Bindings/_about_bindings.txt b/Irony.Interpreter/Bindings/_about_bindings.txt new file mode 100644 index 0000000..d649f46 --- /dev/null +++ b/Irony.Interpreter/Bindings/_about_bindings.txt @@ -0,0 +1,19 @@ +Some vocabulary, to clarify the terms and class names: + +Binding is an object that serves as a link between a symbol and its value. Binding has methods GetValue and SetValue, for setting/getting values into the current context of the app. + For example, symbol X in a script has a corresponding IdentifierNode (AST node). The code in this node, before it can read the value, must get a binding for a symbol "X". On the first execution the node calls a Bind method of the current thread, passing it a BindingRequest object (see below) and expecting back a Binding object that can be used to access the value. + Having a binding object , it can read the value: + var value = binding.GetValue(thread); + +Binding Target Types - classification of bindings by a type of the target. One binding target is a Slot - a local or global variable. Other examples: built-in method; CLR method or object imported through interop. + + +BindingTargetInfo is a metadata for a binding; contains Symbol and BindingType. Each binding has TargetInfo property that describes it. + +BindingRequest is a container for information about a desired binding when the code in AST node tries to get the binding from the executing script environment. It contains Symbol (name of the variable or function), and some other flags. + +IBindingSource is an abstraction of a binding source - something that can produce a binding for a symbol. Simply speaking, binding source is asked "Do you have something named 'foo'?" it answers 'yes, here is a binding to foo thing'. Examples of binding sources: a table of built-in methods; a local frame with a set of variables; import specification in a module pointing to external module. + +BindingSourceTable is a table of binding sources indexed by name. LanguageRuntime.BuiltIns field is such a table - it contains built-in methods and objects, stored as binding sources. + +Scope - a set of variables in a programming scope. For ex: local scope, module scope, object scope. Scope consists of slots - locations where values are stored. Each slot has a SlotInfo meta data object. Each Scope has a ScopeInfo object (metadata) that contains a list of SlotInfo objects desribing slots in the scope. diff --git a/Irony.Interpreter/Diagnostics/ScriptException.cs b/Irony.Interpreter/Diagnostics/ScriptException.cs new file mode 100644 index 0000000..76db85c --- /dev/null +++ b/Irony.Interpreter/Diagnostics/ScriptException.cs @@ -0,0 +1,36 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Interpreter { + public class ScriptException : Exception { + public SourceLocation Location; + public ScriptStackTrace ScriptStackTrace; + public ScriptException(string message) : base(message) { } + public ScriptException(string message, Exception inner) : base(message, inner) { } + public ScriptException(string message, Exception inner, SourceLocation location, ScriptStackTrace stack) + : base(message, inner) { + Location = location; + ScriptStackTrace = stack; + } + + public override string ToString() { + return Message + Environment.NewLine + ScriptStackTrace.ToString(); + } + }//class + +} diff --git a/Irony.Interpreter/Diagnostics/ScriptStackTrace.cs b/Irony.Interpreter/Diagnostics/ScriptStackTrace.cs new file mode 100644 index 0000000..d235822 --- /dev/null +++ b/Irony.Interpreter/Diagnostics/ScriptStackTrace.cs @@ -0,0 +1,21 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter { + public class ScriptStackTrace { + } +} diff --git a/Irony.Interpreter/InterpretedLanguageGrammar.cs b/Irony.Interpreter/InterpretedLanguageGrammar.cs new file mode 100644 index 0000000..f3f9817 --- /dev/null +++ b/Irony.Interpreter/InterpretedLanguageGrammar.cs @@ -0,0 +1,64 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Ast; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + /// Base class for languages that use Irony Interpreter to execute scripts. + public abstract class InterpretedLanguageGrammar : Grammar, ICanRunSample { + // making the class abstract so it won't load into Grammar Explorer + public InterpretedLanguageGrammar(bool caseSensitive) + : base(caseSensitive) { + this.LanguageFlags = LanguageFlags.CreateAst; + } + + // This method allows custom implementation of running a sample in Grammar Explorer + // By default it evaluates a parse tree using default interpreter. + // Irony's interpeter has one restriction: once a script (represented by AST node) is evaluated in ScriptApp, + // its internal fields in AST nodes become tied to this particular instance of ScriptApp (more precisely DataMap). + // If you want to evaluate the AST tree again, you have to do it in the context of the same DataMap. + // Grammar Explorer may call RunSample method repeatedly for evaluation of the same parsed script. So we keep ScriptApp instance in + // the field, and if we get the same script node, then we reuse the ScriptApp thus satisfying the requirement. + private ScriptApp _app; + private ParseTree _prevSample; + + public virtual string RunSample(RunSampleArgs args) { + if (_app == null || args.ParsedSample != _prevSample) + _app = new ScriptApp(args.Language); + _prevSample = args.ParsedSample; + + //for (int i = 0; i < 1000; i++) //for perf measurements, to execute 1000 times + _app.Evaluate(args.ParsedSample); + return _app.OutputBuffer.ToString(); + } + + public virtual LanguageRuntime CreateRuntime(LanguageData language) { + return new LanguageRuntime(language); + } + + public override void BuildAst(LanguageData language, ParseTree parseTree) { + var opHandler = new OperatorHandler(language.Grammar.CaseSensitive); + Util.Check(!parseTree.HasErrors(), "ParseTree has errors, cannot build AST."); + var astContext = new InterpreterAstContext(language, opHandler); + var astBuilder = new AstBuilder(astContext); + astBuilder.BuildAst(parseTree); + } + } //grammar class + +} diff --git a/Irony.Interpreter/LanguageRuntime/LanguageRuntime.cs b/Irony.Interpreter/LanguageRuntime/LanguageRuntime.cs new file mode 100644 index 0000000..5a3eac8 --- /dev/null +++ b/Irony.Interpreter/LanguageRuntime/LanguageRuntime.cs @@ -0,0 +1,82 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Numerics; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + public class ConsoleWriteEventArgs : EventArgs { + public string Text; + public ConsoleWriteEventArgs(string text) { + Text = text; + } + } + + + //Note: mark the derived language-specific class as sealed - important for JIT optimizations + // details here: http://www.codeproject.com/KB/dotnet/JITOptimizations.aspx + public partial class LanguageRuntime { + public readonly LanguageData Language; + public OperatorHandler OperatorHandler; + //Converter of the result for comparison operation; converts bool value to values + // specific for the language + public UnaryOperatorMethod BoolResultConverter = null; + //An unassigned reserved object for a language implementation + public NoneClass NoneValue { get; protected set; } + + //Built-in binding sources + public BindingSourceTable BuiltIns; + + public LanguageRuntime(LanguageData language) { + Language = language; + NoneValue = NoneClass.Value; + BuiltIns = new BindingSourceTable(Language.Grammar.CaseSensitive); + Init(); + } + + public virtual void Init() { + InitOperatorImplementations(); + } + + public virtual bool IsTrue(object value) { + if (value is bool) + return (bool)value; + if (value is int) + return ((int)value != 0); + if(value == NoneValue) + return false; + return value != null; + } + + internal protected void ThrowError(string message, params object[] args) { + if (args != null && args.Length > 0) + message = string.Format(message, args); + throw new Exception(message); + } + + internal protected void ThrowScriptError(string message, params object[] args) { + if (args != null && args.Length > 0) + message = string.Format(message, args); + throw new ScriptException(message); + } + + }//class + +}//namespace + diff --git a/Irony.Interpreter/LanguageRuntime/LanguageRuntime_Binding.cs b/Irony.Interpreter/LanguageRuntime/LanguageRuntime_Binding.cs new file mode 100644 index 0000000..c422bfb --- /dev/null +++ b/Irony.Interpreter/LanguageRuntime/LanguageRuntime_Binding.cs @@ -0,0 +1,108 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + public partial class LanguageRuntime : IBindingSource { + + //Binds to local variables, enclosing scopes, module scopes/globals and built-ins + public virtual Binding Bind(BindingRequest request) { + var symbol = request.Symbol; + var mode = request.Flags; + if (mode.IsSet(BindingRequestFlags.Write)) + return BindSymbolForWrite(request); + else if (mode.IsSet(BindingRequestFlags.Read)) + return BindSymbolForRead(request); + else { + //TODO: need to throw fatal error here + request.Thread.ThrowScriptError("Invalid binding request, access type (Read or Write) is not set in request Options."); + return null; // never happens + } + }//method + + public virtual Binding BindSymbolForWrite(BindingRequest request) { + var scope = request.Thread.CurrentScope; + var existingSlot = scope.Info.GetSlot(request.Symbol); + //1. If new only, check it does not exist yet, create and return it + if (request.Flags.IsSet(BindingRequestFlags.NewOnly)) { + if (existingSlot != null) + request.Thread.ThrowScriptError("Variable {0} already exists.", request.Symbol); + var newSlot = scope.AddSlot(request.Symbol); + return new SlotBinding(newSlot, request.FromNode, request.FromScopeInfo); + } + //2. If exists, then return it + if (existingSlot != null && request.Flags.IsSet(BindingRequestFlags.ExistingOrNew)) { + //TODO: For external client, check that slot is actually public or exported + return new SlotBinding(existingSlot, request.FromNode, request.FromScopeInfo); + } + + //3. Check external module imports + foreach (var imp in request.FromModule.Imports) { + var result = imp.Bind(request); + if (result != null) + return result; + } + + //4. If nothing found, create new slot in current scope + if (request.Flags.IsSet(BindingRequestFlags.ExistingOrNew)) { + var newSlot = scope.AddSlot(request.Symbol); + return new SlotBinding(newSlot, request.FromNode, request.FromScopeInfo); + } + + //5. Check built-in methods + var builtIn = BuiltIns.Bind(request); + if (builtIn != null) return builtIn; + + //6. If still not found, return null. + return null; + }//method + + public virtual Binding BindSymbolForRead(BindingRequest request) { + var symbol = request.Symbol; + // First check current and enclosing scopes + var currScope = request.Thread.CurrentScope; + do { + var existingSlot = currScope.Info.GetSlot(symbol); + if (existingSlot != null) + return new SlotBinding(existingSlot, request.FromNode, request.FromScopeInfo); + currScope = currScope.Parent; + } while (currScope != null); + + // If not found, check imports + foreach (var imp in request.FromModule.Imports) { + var result = imp.Bind(request); + if (result != null) + return result; + } + + // Check built-in modules + var builtIn = BuiltIns.Bind(request); + if (builtIn != null) return builtIn; + + // if not found, return null + return null; + } + + //Binds symbol to a public member exported by a module. + public virtual Binding BindSymbol(BindingRequest request, ModuleInfo module) { + return module.BindToExport(request); + } + + + }//class +} diff --git a/Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch.cs b/Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch.cs new file mode 100644 index 0000000..cacf464 --- /dev/null +++ b/Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch.cs @@ -0,0 +1,119 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Numerics; + +namespace Irony.Interpreter { + + public partial class LanguageRuntime { + public readonly OperatorImplementationTable OperatorImplementations = new OperatorImplementationTable(2000); + + + public object ExecuteBinaryOperator(ExpressionType op, object arg1, object arg2, ref OperatorImplementation previousUsed) { + // 1. Get arg types + Type arg1Type, arg2Type; + try { + arg1Type = arg1.GetType(); + arg2Type = arg2.GetType(); + } catch (NullReferenceException) { + // arg1 or arg2 is null - which means never assigned. + CheckUnassigned(arg1); + CheckUnassigned(arg2); + throw; + } + + // 2. If we had prev impl, check if current args types match it; first copy it into local variable + // Note: BinaryExpression node might already have tried it directly, without any checks, and + // apparently failed. At some point this attempt in BinaryExpressionNode can become disabled. + // But we might still try it here, with proper checks + var currentImpl = previousUsed; + if (currentImpl != null && (arg1Type != currentImpl.Key.Arg1Type || arg2Type != currentImpl.Key.Arg2Type)) + currentImpl = null; + + // 3. Find implementation for arg types + OperatorDispatchKey key; + if (currentImpl == null) { + key = new OperatorDispatchKey(op, arg1Type, arg2Type); + if (!OperatorImplementations.TryGetValue(key, out currentImpl)) + ThrowScriptError(Resources.ErrOpNotDefinedForTypes, op, arg1Type, arg2Type); + } + + // 4. Actually call + try { + previousUsed = currentImpl; + return currentImpl.EvaluateBinary(arg1, arg2); + } catch (OverflowException) { + if (currentImpl.OverflowHandler == null) throw; + previousUsed = currentImpl.OverflowHandler; //set previousUsed to overflowHandler, so it will be used next time + return ExecuteBinaryOperator(op, arg1, arg2, ref previousUsed); //call self recursively + } catch(IndexOutOfRangeException) { + //We can get here only if we use SmartBoxing - the result is out of range of pre-allocated boxes, + // so attempt to lookup a boxed value in _boxes dictionary fails with outOfRange exc + if (currentImpl.NoBoxImplementation == null) throw; + // If NoBoxImpl is not null, then it is implementation with auto-boxing. + // Auto-boxing failed - the result is outside the range of our boxes array. Let's call no-box version. + // we also set previousUsed to no-box implementation, so we use it in the future calls + previousUsed = currentImpl.NoBoxImplementation; + return ExecuteBinaryOperator(op, arg1, arg2, ref previousUsed); //call self recursively + } + + }//method + + public object ExecuteUnaryOperator(ExpressionType op, object arg1, ref OperatorImplementation previousUsed) { + // 1. Get arg type + Type arg1Type; + try { + arg1Type = arg1.GetType(); + } catch (NullReferenceException) { + CheckUnassigned(arg1); + throw; + } + + // 2. If we had prev impl, check if current args types match it; first copy it into local variable + OperatorDispatchKey key; + var currentImpl = previousUsed; + if (currentImpl != null && arg1Type != currentImpl.Key.Arg1Type) + currentImpl = null; + + // 3. Find implementation for arg type + if (currentImpl == null) { + key = new OperatorDispatchKey(op, arg1Type); + if (!OperatorImplementations.TryGetValue(key, out currentImpl)) + ThrowError(Resources.ErrOpNotDefinedForType, op, arg1Type); + } + + // 4. Actually call + try { + previousUsed = currentImpl; //set previousUsed so next time we'll try this impl first + return currentImpl.Arg1Converter(arg1); + } catch (OverflowException) { + if (currentImpl.OverflowHandler == null) + throw; + previousUsed = currentImpl.OverflowHandler; //set previousUsed to overflowHandler, so it will be used next time + return ExecuteUnaryOperator(op, arg1, ref previousUsed); //call self recursively + } + }//method + + + //TODO: finish this + private void CheckUnassigned(object value) { + if (value == null) + throw new Exception("Variable unassigned."); + } + + }//class +} diff --git a/Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch_Init.cs b/Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch_Init.cs new file mode 100644 index 0000000..37c2562 --- /dev/null +++ b/Irony.Interpreter/LanguageRuntime/LanguageRuntime_OpDispatch_Init.cs @@ -0,0 +1,659 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Numerics; +using System.Diagnostics; +using Irony.Parsing; + +namespace Irony.Interpreter { + + //Initialization of Runtime + public partial class LanguageRuntime { + private static ExpressionType[] _overflowOperators = new ExpressionType[] { + ExpressionType.Add, ExpressionType.AddChecked, ExpressionType.Subtract, ExpressionType.SubtractChecked, + ExpressionType.Multiply, ExpressionType.MultiplyChecked, ExpressionType.Power}; + + // Smart boxing: boxes for a bunch of integers are preallocated + private object[] _boxes = new object[4096]; + private const int _boxesMiddle = 2048; + // Note: ran some primitive tests, and it appears that use of smart boxing makes it slower + // by about 5-10%; so disabling it for now + public bool SmartBoxingEnabled = false; + bool _supportsComplex; + bool _supportsBigInt; + bool _supportsRational; + + + protected virtual void InitOperatorImplementations() { + _supportsComplex = this.Language.Grammar.LanguageFlags.IsSet(LanguageFlags.SupportsComplex); + _supportsBigInt = this.Language.Grammar.LanguageFlags.IsSet(LanguageFlags.SupportsBigInt); + _supportsRational = this.Language.Grammar.LanguageFlags.IsSet(LanguageFlags.SupportsRational); + // TODO: add support for Rational + if (SmartBoxingEnabled) + InitBoxes(); + InitTypeConverters(); + InitBinaryOperatorImplementationsForMatchedTypes(); + InitUnaryOperatorImplementations(); + CreateBinaryOperatorImplementationsForMismatchedTypes(); + CreateOverflowHandlers(); + } + + //The value of smart boxing is questionable - so far did not see perf improvements, so currently it is disabled + private void InitBoxes() { + for (int i = 0; i < _boxes.Length; i++) + _boxes[i] = i - _boxesMiddle; + } + + #region Utility methods for adding converters and binary implementations + protected OperatorImplementation AddConverter(Type fromType, Type toType, UnaryOperatorMethod method) { + var key = new OperatorDispatchKey(ExpressionType.ConvertChecked, fromType, toType); + var impl = new OperatorImplementation(key, toType, method); + OperatorImplementations[key] = impl; + return impl; + } + + protected OperatorImplementation AddBinaryBoxed(ExpressionType op, Type baseType, + BinaryOperatorMethod boxedBinaryMethod, BinaryOperatorMethod noBoxMethod) { + // first create implementation without boxing + var noBoxImpl = AddBinary(op, baseType, noBoxMethod); + if (!SmartBoxingEnabled) + return noBoxImpl; + //The boxedImpl will overwrite noBoxImpl in the dictionary + var boxedImpl = AddBinary(op, baseType, boxedBinaryMethod); + boxedImpl.NoBoxImplementation = noBoxImpl; + return boxedImpl; + } + + protected OperatorImplementation AddBinary(ExpressionType op, Type baseType, BinaryOperatorMethod binaryMethod) { + return AddBinary(op, baseType, binaryMethod, null); + } + + protected OperatorImplementation AddBinary(ExpressionType op, Type commonType, + BinaryOperatorMethod binaryMethod, UnaryOperatorMethod resultConverter) { + var key = new OperatorDispatchKey(op, commonType, commonType); + var impl = new OperatorImplementation(key, commonType, binaryMethod, null, null, resultConverter); + OperatorImplementations[key] = impl; + return impl; + } + + protected OperatorImplementation AddUnary(ExpressionType op, Type commonType, UnaryOperatorMethod unaryMethod) { + var key = new OperatorDispatchKey(op, commonType); + var impl = new OperatorImplementation(key, commonType, null, unaryMethod, null, null); + OperatorImplementations[key] = impl; + return impl; + } + + #endregion + + #region Initializing type converters + public virtual void InitTypeConverters() { + Type targetType; + + //->string + targetType = typeof(string); + AddConverter(typeof(char), targetType, ConvertAnyToString); + AddConverter(typeof(sbyte), targetType, ConvertAnyToString); + AddConverter(typeof(byte), targetType, ConvertAnyToString); + AddConverter(typeof(Int16), targetType, ConvertAnyToString); + AddConverter(typeof(UInt16), targetType, ConvertAnyToString); + AddConverter(typeof(Int32), targetType, ConvertAnyToString); + AddConverter(typeof(UInt32), targetType, ConvertAnyToString); + AddConverter(typeof(Int64), targetType, ConvertAnyToString); + AddConverter(typeof(UInt64), targetType, ConvertAnyToString); + AddConverter(typeof(Single), targetType, ConvertAnyToString); + if (_supportsBigInt) + AddConverter(typeof(BigInteger), targetType, ConvertAnyToString); + if (_supportsComplex) + AddConverter(typeof(Complex), targetType, ConvertAnyToString); + + //->Complex + if (_supportsComplex) { + targetType = typeof(Complex); + AddConverter(typeof(sbyte), targetType, ConvertAnyToComplex); + AddConverter(typeof(byte), targetType, ConvertAnyToComplex); + AddConverter(typeof(Int16), targetType, ConvertAnyToComplex); + AddConverter(typeof(UInt16), targetType, ConvertAnyToComplex); + AddConverter(typeof(Int32), targetType, ConvertAnyToComplex); + AddConverter(typeof(UInt32), targetType, ConvertAnyToComplex); + AddConverter(typeof(Int64), targetType, ConvertAnyToComplex); + AddConverter(typeof(UInt64), targetType, ConvertAnyToComplex); + AddConverter(typeof(Single), targetType, ConvertAnyToComplex); + if (_supportsBigInt) + AddConverter(typeof(BigInteger), targetType, ConvertBigIntToComplex); + } + //->BigInteger + if (_supportsBigInt) { + targetType = typeof(BigInteger); + AddConverter(typeof(sbyte), targetType, ConvertAnyIntToBigInteger); + AddConverter(typeof(byte), targetType, ConvertAnyIntToBigInteger); + AddConverter(typeof(Int16), targetType, ConvertAnyIntToBigInteger); + AddConverter(typeof(UInt16), targetType, ConvertAnyIntToBigInteger); + AddConverter(typeof(Int32), targetType, ConvertAnyIntToBigInteger); + AddConverter(typeof(UInt32), targetType, ConvertAnyIntToBigInteger); + AddConverter(typeof(Int64), targetType, ConvertAnyIntToBigInteger); + AddConverter(typeof(UInt64), targetType, ConvertAnyIntToBigInteger); + } + + //->Double + targetType = typeof(double); + AddConverter(typeof(sbyte), targetType, value => (double)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (double)(byte)value); + AddConverter(typeof(Int16), targetType, value => (double)(Int16)value); + AddConverter(typeof(UInt16), targetType, value => (double)(UInt16)value); + AddConverter(typeof(Int32), targetType, value => (double)(Int32)value); + AddConverter(typeof(UInt32), targetType, value => (double)(UInt32)value); + AddConverter(typeof(Int64), targetType, value => (double)(Int64)value); + AddConverter(typeof(UInt64), targetType, value => (double)(UInt64)value); + AddConverter(typeof(Single), targetType, value => (double)(Single)value); + if (_supportsBigInt) + AddConverter(typeof(BigInteger), targetType, value => ((double) (BigInteger)value)); + + //->Single + targetType = typeof(Single); + AddConverter(typeof(sbyte), targetType, value => (Single)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (Single)(byte)value); + AddConverter(typeof(Int16), targetType, value => (Single)(Int16)value); + AddConverter(typeof(UInt16), targetType, value => (Single)(UInt16)value); + AddConverter(typeof(Int32), targetType, value => (Single)(Int32)value); + AddConverter(typeof(UInt32), targetType, value => (Single)(UInt32)value); + AddConverter(typeof(Int64), targetType, value => (Single)(Int64)value); + AddConverter(typeof(UInt64), targetType, value => (Single)(UInt64)value); + if (_supportsBigInt) + AddConverter(typeof(BigInteger), targetType, value => (Single)(BigInteger)value); + + //->UInt64 + targetType = typeof(UInt64); + AddConverter(typeof(sbyte), targetType, value => (UInt64)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (UInt64)(byte)value); + AddConverter(typeof(Int16), targetType, value => (UInt64)(Int16)value); + AddConverter(typeof(UInt16), targetType, value => (UInt64)(UInt16)value); + AddConverter(typeof(Int32), targetType, value => (UInt64)(Int32)value); + AddConverter(typeof(UInt32), targetType, value => (UInt64)(UInt32)value); + AddConverter(typeof(Int64), targetType, value => (UInt64)(Int64)value); + + //->Int64 + targetType = typeof(Int64); + AddConverter(typeof(sbyte), targetType, value => (Int64)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (Int64)(byte)value); + AddConverter(typeof(Int16), targetType, value => (Int64)(Int16)value); + AddConverter(typeof(UInt16), targetType, value => (Int64)(UInt16)value); + AddConverter(typeof(Int32), targetType, value => (Int64)(Int32)value); + AddConverter(typeof(UInt32), targetType, value => (Int64)(UInt32)value); + + //->UInt32 + targetType = typeof(UInt32); + AddConverter(typeof(sbyte), targetType, value => (UInt32)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (UInt32)(byte)value); + AddConverter(typeof(Int16), targetType, value => (UInt32)(Int16)value); + AddConverter(typeof(UInt16), targetType, value => (UInt32)(UInt16)value); + AddConverter(typeof(Int32), targetType, value => (UInt32)(Int32)value); + + //->Int32 + targetType = typeof(Int32); + AddConverter(typeof(sbyte), targetType, value => (Int32)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (Int32)(byte)value); + AddConverter(typeof(Int16), targetType, value => (Int32)(Int16)value); + AddConverter(typeof(UInt16), targetType, value => (Int32)(UInt16)value); + + //->UInt16 + targetType = typeof(UInt16); + AddConverter(typeof(sbyte), targetType, value => (UInt16)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (UInt16)(byte)value); + AddConverter(typeof(Int16), targetType, value => (UInt16)(Int16)value); + + //->Int16 + targetType = typeof(Int16); + AddConverter(typeof(sbyte), targetType, value => (Int16)(sbyte)value); + AddConverter(typeof(byte), targetType, value => (Int16)(byte)value); + + //->byte + targetType = typeof(byte); + AddConverter(typeof(sbyte), targetType, value => (byte)(sbyte)value); + } + + // Some specialized convert implementation methods + public static object ConvertAnyToString(object value) { + return value == null ? string.Empty : value.ToString(); + } + + public static object ConvertBigIntToComplex(object value) { + BigInteger bi = (BigInteger)value; + return new Complex((double) bi, 0); + } + + public static object ConvertAnyToComplex(object value) { + double d = Convert.ToDouble(value); + return new Complex(d, 0); + } + public static object ConvertAnyIntToBigInteger(object value) { + long l = Convert.ToInt64(value); + return new BigInteger(l); + } + #endregion + + #region Binary operators implementations + // Generates of binary implementations for matched argument types + public virtual void InitBinaryOperatorImplementationsForMatchedTypes() { + + // For each operator, we add a series of implementation methods for same-type operands. They are saved as OperatorImplementation + // records in OperatorImplementations table. This happens at initialization time. + // After this initialization (for same-type operands), system adds implementations for all type pairs (ex: int + double), + // using these same-type implementations and appropriate type converters. + // Note that arithmetics on byte, sbyte, int16, uint16 are performed in Int32 format (the way it's done in c# I guess) + // so the result is always Int32. We do not define operators for sbyte, byte, int16 and UInt16 types - they will + // be processed using Int32 implementation, with appropriate type converters. + ExpressionType op; + + op = ExpressionType.AddChecked; + AddBinaryBoxed(op, typeof(Int32), (x, y) => _boxes[checked((Int32)x + (Int32)y) + _boxesMiddle], + (x, y) => checked((Int32)x + (Int32)y)); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x + (UInt32)y)); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x + (Int64)y)); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x + (UInt64)y)); + AddBinary(op, typeof(Single), (x, y) => (Single)x + (Single)y); + AddBinary(op, typeof(double), (x, y) => (double)x + (double)y); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x + (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x + (BigInteger)y); + if (_supportsComplex) + AddBinary(op, typeof(Complex), (x, y) => (Complex)x + (Complex)y); + AddBinary(op, typeof(string), (x, y) => (string)x + (string)y); + AddBinary(op, typeof(char), (x, y) => ((char)x).ToString() + (char)y); //force to concatenate as strings + + op = ExpressionType.SubtractChecked; + AddBinaryBoxed(op, typeof(Int32), (x, y) => _boxes[checked((Int32)x - (Int32)y) + _boxesMiddle], + (x, y) => checked((Int32)x - (Int32)y)); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x - (UInt32)y)); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x - (Int64)y)); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x - (UInt64)y)); + AddBinary(op, typeof(Single), (x, y) => (Single)x - (Single)y); + AddBinary(op, typeof(double), (x, y) => (double)x - (double)y); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x - (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x - (BigInteger)y); + if (_supportsComplex) + AddBinary(op, typeof(Complex), (x, y) => (Complex)x - (Complex)y); + + op = ExpressionType.MultiplyChecked; + AddBinaryBoxed(op, typeof(Int32), (x, y) => _boxes[checked((Int32)x * (Int32)y) + _boxesMiddle], + (x, y) => checked((Int32)x * (Int32)y)); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x * (UInt32)y)); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x * (Int64)y)); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x * (UInt64)y)); + AddBinary(op, typeof(Single), (x, y) => (Single)x * (Single)y); + AddBinary(op, typeof(double), (x, y) => (double)x * (double)y); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x * (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x * (BigInteger)y); + if (_supportsComplex) + AddBinary(op, typeof(Complex), (x, y) => (Complex)x * (Complex)y); + + op = ExpressionType.Divide; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x / (Int32)y)); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x / (UInt32)y)); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x / (Int64)y)); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x / (UInt64)y)); + AddBinary(op, typeof(Single), (x, y) => (Single)x / (Single)y); + AddBinary(op, typeof(double), (x, y) => (double)x / (double)y); + AddBinary(op, typeof(decimal), (x, y) =>(decimal)x / (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x / (BigInteger)y); + if (_supportsComplex) + AddBinary(op, typeof(Complex), (x, y) => (Complex)x / (Complex)y); + + op = ExpressionType.Modulo; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x % (Int32)y)); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x % (UInt32)y)); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x % (Int64)y)); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x % (UInt64)y)); + AddBinary(op, typeof(Single), (x, y) => (Single)x % (Single)y); + AddBinary(op, typeof(double), (x, y) => (double)x % (double)y); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x % (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x % (BigInteger)y); + + // For bitwise operator, we provide explicit implementations for "small" integer types + op = ExpressionType.And; + AddBinary(op, typeof(bool), (x, y) => (bool)x & (bool)y); + AddBinary(op, typeof(sbyte), (x, y) => (sbyte)x & (sbyte)y); + AddBinary(op, typeof(byte), (x, y) => (byte)x & (byte)y); + AddBinary(op, typeof(Int16), (x, y) => (Int16)x & (Int16)y); + AddBinary(op, typeof(UInt16), (x, y) => (UInt16)x & (UInt16)y); + AddBinary(op, typeof(Int32), (x, y) => (Int32)x & (Int32)y); + AddBinary(op, typeof(UInt32), (x, y) => (UInt32)x & (UInt32)y); + AddBinary(op, typeof(Int64), (x, y) => (Int64)x & (Int64)y); + AddBinary(op, typeof(UInt64), (x, y) => (UInt64)x & (UInt64)y); + + op = ExpressionType.Or; + AddBinary(op, typeof(bool), (x, y) => (bool)x | (bool)y); + AddBinary(op, typeof(sbyte), (x, y) => (sbyte)x | (sbyte)y); + AddBinary(op, typeof(byte), (x, y) => (byte)x | (byte)y); + AddBinary(op, typeof(Int16), (x, y) => (Int16)x | (Int16)y); + AddBinary(op, typeof(UInt16), (x, y) => (UInt16)x | (UInt16)y); + AddBinary(op, typeof(Int32), (x, y) => (Int32)x | (Int32)y); + AddBinary(op, typeof(UInt32), (x, y) => (UInt32)x | (UInt32)y); + AddBinary(op, typeof(Int64), (x, y) => (Int64)x | (Int64)y); + AddBinary(op, typeof(UInt64), (x, y) => (UInt64)x | (UInt64)y); + + op = ExpressionType.ExclusiveOr; + AddBinary(op, typeof(bool), (x, y) => (bool)x ^ (bool)y); + AddBinary(op, typeof(sbyte), (x, y) => (sbyte)x ^ (sbyte)y); + AddBinary(op, typeof(byte), (x, y) => (byte)x ^ (byte)y); + AddBinary(op, typeof(Int16), (x, y) => (Int16)x ^ (Int16)y); + AddBinary(op, typeof(UInt16), (x, y) => (UInt16)x ^ (UInt16)y); + AddBinary(op, typeof(Int32), (x, y) => (Int32)x ^ (Int32)y); + AddBinary(op, typeof(UInt32), (x, y) => (UInt32)x ^ (UInt32)y); + AddBinary(op, typeof(Int64), (x, y) => (Int64)x ^ (Int64)y); + AddBinary(op, typeof(UInt64), (x, y) => (UInt64)x ^ (UInt64)y); + + op = ExpressionType.LessThan; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x < (Int32)y), BoolResultConverter); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x < (UInt32)y), BoolResultConverter); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x < (Int64)y), BoolResultConverter); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x < (UInt64)y), BoolResultConverter); + AddBinary(op, typeof(Single), (x, y) => (Single)x < (Single)y, BoolResultConverter); + AddBinary(op, typeof(double), (x, y) => (double)x < (double)y, BoolResultConverter); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x < (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x < (BigInteger)y, BoolResultConverter); + + op = ExpressionType.GreaterThan; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x > (Int32)y), BoolResultConverter); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x > (UInt32)y), BoolResultConverter); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x > (Int64)y), BoolResultConverter); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x > (UInt64)y), BoolResultConverter); + AddBinary(op, typeof(Single), (x, y) => (Single)x > (Single)y, BoolResultConverter); + AddBinary(op, typeof(double), (x, y) => (double)x > (double)y, BoolResultConverter); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x > (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x > (BigInteger)y, BoolResultConverter); + + op = ExpressionType.LessThanOrEqual; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x <= (Int32)y), BoolResultConverter); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x <= (UInt32)y), BoolResultConverter); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x <= (Int64)y), BoolResultConverter); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x <= (UInt64)y), BoolResultConverter); + AddBinary(op, typeof(Single), (x, y) => (Single)x <= (Single)y, BoolResultConverter); + AddBinary(op, typeof(double), (x, y) => (double)x <= (double)y, BoolResultConverter); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x <= (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x <= (BigInteger)y, BoolResultConverter); + + op = ExpressionType.GreaterThanOrEqual; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x >= (Int32)y), BoolResultConverter); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x >= (UInt32)y), BoolResultConverter); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x >= (Int64)y), BoolResultConverter); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x >= (UInt64)y), BoolResultConverter); + AddBinary(op, typeof(Single), (x, y) => (Single)x >= (Single)y, BoolResultConverter); + AddBinary(op, typeof(double), (x, y) => (double)x >= (double)y, BoolResultConverter); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x >= (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x >= (BigInteger)y, BoolResultConverter); + + op = ExpressionType.Equal; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x == (Int32)y), BoolResultConverter); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x == (UInt32)y), BoolResultConverter); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x == (Int64)y), BoolResultConverter); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x == (UInt64)y), BoolResultConverter); + AddBinary(op, typeof(Single), (x, y) => (Single)x == (Single)y, BoolResultConverter); + AddBinary(op, typeof(double), (x, y) => (double)x == (double)y, BoolResultConverter); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x == (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x == (BigInteger)y, BoolResultConverter); + + op = ExpressionType.NotEqual; + AddBinary(op, typeof(Int32), (x, y) => checked((Int32)x != (Int32)y), BoolResultConverter); + AddBinary(op, typeof(UInt32), (x, y) => checked((UInt32)x != (UInt32)y), BoolResultConverter); + AddBinary(op, typeof(Int64), (x, y) => checked((Int64)x != (Int64)y), BoolResultConverter); + AddBinary(op, typeof(UInt64), (x, y) => checked((UInt64)x != (UInt64)y), BoolResultConverter); + AddBinary(op, typeof(Single), (x, y) => (Single)x != (Single)y, BoolResultConverter); + AddBinary(op, typeof(double), (x, y) => (double)x != (double)y, BoolResultConverter); + AddBinary(op, typeof(decimal), (x, y) => (decimal)x != (decimal)y); + if (_supportsBigInt) + AddBinary(op, typeof(BigInteger), (x, y) => (BigInteger)x != (BigInteger)y, BoolResultConverter); + + }//method + + public virtual void InitUnaryOperatorImplementations() { + var op = ExpressionType.UnaryPlus; + AddUnary(op, typeof(sbyte), x => +(sbyte)x); + AddUnary(op, typeof(byte), x => +(byte)x); + AddUnary(op, typeof(Int16), x => +(Int16)x); + AddUnary(op, typeof(UInt16), x => +(UInt16)x); + AddUnary(op, typeof(Int32), x => +(Int32)x); + AddUnary(op, typeof(UInt32), x => +(UInt32)x); + AddUnary(op, typeof(Int64), x => +(Int64)x); + AddUnary(op, typeof(UInt64), x => +(UInt64)x); + AddUnary(op, typeof(Single), x => +(Single)x); + AddUnary(op, typeof(double), x => +(double)x); + AddUnary(op, typeof(decimal), x => +(decimal)x); + if (_supportsBigInt) + AddUnary(op, typeof(BigInteger), x => +(BigInteger)x); + + op = ExpressionType.Negate; + AddUnary(op, typeof(sbyte), x => -(sbyte)x); + AddUnary(op, typeof(byte), x => -(byte)x); + AddUnary(op, typeof(Int16), x => -(Int16)x); + AddUnary(op, typeof(UInt16), x => -(UInt16)x); + AddUnary(op, typeof(Int32), x => -(Int32)x); + AddUnary(op, typeof(UInt32), x => -(UInt32)x); + AddUnary(op, typeof(Int64), x => -(Int64)x); + AddUnary(op, typeof(Single), x => -(Single)x); + AddUnary(op, typeof(double), x => -(double)x); + AddUnary(op, typeof(decimal), x => -(decimal)x); + if (_supportsBigInt) + AddUnary(op, typeof(BigInteger), x => -(BigInteger)x); + if (_supportsComplex) + AddUnary(op, typeof(Complex), x => -(Complex)x); + + op = ExpressionType.Not; + AddUnary(op, typeof(bool), x => !(bool)x); + AddUnary(op, typeof(sbyte), x => ~(sbyte)x); + AddUnary(op, typeof(byte), x => ~(byte)x); + AddUnary(op, typeof(Int16), x => ~(Int16)x); + AddUnary(op, typeof(UInt16), x => ~(UInt16)x); + AddUnary(op, typeof(Int32), x => ~(Int32)x); + AddUnary(op, typeof(UInt32), x => ~(UInt32)x); + AddUnary(op, typeof(Int64), x => ~(Int64)x); + + } + + // Generates binary implementations for mismatched argument types + public virtual void CreateBinaryOperatorImplementationsForMismatchedTypes() { + // find all data types are there + var allTypes = new HashSet(); + var allBinOps = new HashSet(); + foreach (var kv in OperatorImplementations) { + allTypes.Add(kv.Key.Arg1Type); + if (kv.Value.BaseBinaryMethod != null) + allBinOps.Add(kv.Key.Op); + } + foreach (var arg1Type in allTypes) + foreach (var arg2Type in allTypes) + if (arg1Type != arg2Type) + foreach (ExpressionType op in allBinOps) + CreateBinaryOperatorImplementation(op, arg1Type, arg2Type); + }//method + + // Creates a binary implementations for an operator with mismatched argument types. + // Determines common type, retrieves implementation for operator with both args of common type, then creates + // implementation for mismatched types using type converters (by converting to common type) + public OperatorImplementation CreateBinaryOperatorImplementation(ExpressionType op, Type arg1Type, Type arg2Type) { + Type commonType = GetCommonTypeForOperator(op, arg1Type, arg2Type); + if (commonType == null) + return null; + //Get base method for the operator and common type + var baseImpl = FindBaseImplementation(op, commonType); + if (baseImpl == null) { //Try up-type + commonType = GetUpType(commonType); + if (commonType == null) + return null; + baseImpl = FindBaseImplementation(op, commonType); + } + if (baseImpl == null) + return null; + //Create implementation and save it in implementations table + var impl = CreateBinaryOperatorImplementation(op, arg1Type, arg2Type, commonType, baseImpl.BaseBinaryMethod, baseImpl.ResultConverter); + OperatorImplementations[impl.Key] = impl; + return impl; + } + + protected virtual OperatorImplementation CreateBinaryOperatorImplementation(ExpressionType op, Type arg1Type, Type arg2Type, + Type commonType, BinaryOperatorMethod method, UnaryOperatorMethod resultConverter) { + OperatorDispatchKey key = new OperatorDispatchKey(op, arg1Type, arg2Type); + UnaryOperatorMethod arg1Converter = arg1Type == commonType ? null : GetConverter(arg1Type, commonType); + UnaryOperatorMethod arg2Converter = arg2Type == commonType ? null : GetConverter(arg2Type, commonType); + var impl = new OperatorImplementation( + key, commonType, method, arg1Converter, arg2Converter, resultConverter); + return impl; + } + + // Creates overflow handlers. For each implementation, checks if operator can overflow; + // if yes, creates and sets an overflow handler - another implementation that performs + // operation using "upper" type that wouldn't overflow. For ex: (int * int) has overflow handler (int64 * int64) + protected virtual void CreateOverflowHandlers() { + foreach (var impl in OperatorImplementations.Values) { + if (!CanOverflow(impl)) + continue; + var key = impl.Key; + var upType = GetUpType(impl.CommonType); + if (upType == null) + continue; + var upBaseImpl = FindBaseImplementation(key.Op, upType); + if (upBaseImpl == null) + continue; + impl.OverflowHandler = CreateBinaryOperatorImplementation(key.Op, key.Arg1Type, key.Arg2Type, upType, + upBaseImpl.BaseBinaryMethod, upBaseImpl.ResultConverter); + // Do not put OverflowHandler into OperatoImplementations table! - it will override some other, non-overflow impl + } + } + + private OperatorImplementation FindBaseImplementation(ExpressionType op, Type commonType) { + var baseKey = new OperatorDispatchKey(op, commonType, commonType); + OperatorImplementation baseImpl; + OperatorImplementations.TryGetValue(baseKey, out baseImpl); + return baseImpl; + } + + // Important: returns null if fromType == toType + public virtual UnaryOperatorMethod GetConverter(Type fromType, Type toType) { + if (fromType == toType) + return (x => x); + var key = new OperatorDispatchKey(ExpressionType.ConvertChecked, fromType, toType); + OperatorImplementation impl; + if (!OperatorImplementations.TryGetValue(key, out impl)) + return null; + return impl.Arg1Converter; + } + #endregion + + #region Utilities + + private static bool CanOverflow(OperatorImplementation impl) { + if (!CanOverflow(impl.Key.Op)) + return false; + if (impl.CommonType == typeof(Int32) && IsSmallInt(impl.Key.Arg1Type) && IsSmallInt(impl.Key.Arg2Type)) + return false; + if (impl.CommonType == typeof(double) || impl.CommonType == typeof(Single)) + return false; + if (impl.CommonType == typeof(BigInteger)) + return false; + return true; + } + + private static bool CanOverflow(ExpressionType expression) { + return _overflowOperators.Contains(expression); + } + + + private static bool IsSmallInt(Type type) { + return type == typeof(byte) || type == typeof(sbyte) || type == typeof(Int16) || type == typeof(UInt16); + } + + /// + /// Returns the type to which arguments should be converted to perform the operation + /// for a given operator and arguments types. + /// + /// Operator. + /// The type of the first argument. + /// The type of the second argument + /// A common type for operation. + protected virtual Type GetCommonTypeForOperator(ExpressionType op, Type argType1, Type argType2) { + if (argType1 == argType2) + return argType1; + + //TODO: see how to handle properly null/NoneValue in expressions + // var noneType = typeof(NoneClass); + // if (argType1 == noneType || argType2 == noneType) return noneType; + + // Check for unsigned types and convert to signed versions + var t1 = GetSignedTypeForUnsigned(argType1); + var t2 = GetSignedTypeForUnsigned(argType2); + // The type with higher index in _typesSequence is the commont type + var index1 = _typesSequence.IndexOf(t1); + var index2 = _typesSequence.IndexOf(t2); + if (index1 >= 0 && index2 >= 0) + return _typesSequence[Math.Max(index1, index2)]; + //If we have some custom type, + return null; + }//method + + // If a type is one of "unsigned" int types, returns next bigger signed type + protected virtual Type GetSignedTypeForUnsigned(Type type) { + if (!_unsignedTypes.Contains(type)) return type; + if (type == typeof(byte) || type == typeof(UInt16)) return typeof(int); + if (type == typeof(UInt32)) return typeof(Int64); + if (type == typeof(UInt64)) return typeof(Int64); //let's remain in Int64 + return typeof(BigInteger); + } + + /// + /// Returns the "up-type" to use in operation instead of the type that caused overflow. + /// + /// The base type for operation that caused overflow. + /// The type to use for operation. + /// + /// Can be overwritten in language implementation to implement different type-conversion policy. + /// + protected virtual Type GetUpType(Type type) { + // In fact we do not need to care about unsigned types - they are eliminated from common types for operations, + // so "type" parameter can never be unsigned type. But just in case... + if (_unsignedTypes.Contains(type)) + return GetSignedTypeForUnsigned(type); //it will return "upped" type in fact + if (type == typeof(byte) || type == typeof(sbyte) || type == typeof(UInt16) || type == typeof(Int16)) + return typeof(int); + if (type == typeof(Int32)) + return typeof(Int64); + if (type == typeof(Int64)) + return typeof(BigInteger); + return null; + } + + //Note bool type at the end - if any of operands is of bool type, convert the other to bool as well + static TypeList _typesSequence = new TypeList( + typeof(sbyte), typeof(Int16), typeof(Int32), typeof(Int64), typeof(BigInteger), // typeof(Rational) + typeof(Single), typeof(Double), typeof(Complex), + typeof(bool), typeof(char), typeof(string) + ); + static TypeList _unsignedTypes = new TypeList( + typeof(byte), typeof(UInt16), typeof(UInt32), typeof(UInt64) + ); + #endregion + + }//class + +}//namespace diff --git a/Irony.Interpreter/LanguageRuntime/NoneClass.cs b/Irony.Interpreter/LanguageRuntime/NoneClass.cs new file mode 100644 index 0000000..839216c --- /dev/null +++ b/Irony.Interpreter/LanguageRuntime/NoneClass.cs @@ -0,0 +1,40 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter { + + // A class for special reserved None value used in many scripting languages. + public class NoneClass { + string _toString; + + private NoneClass() { + _toString = Resources.LabelNone; + } + public NoneClass(string toString) { + _toString = toString; + } + public override string ToString() { + return _toString; + } + + public static NoneClass Value = new NoneClass(); + } + + + +} diff --git a/Irony.Interpreter/LanguageRuntime/OperatorImplementation.cs b/Irony.Interpreter/LanguageRuntime/OperatorImplementation.cs new file mode 100644 index 0000000..50697e9 --- /dev/null +++ b/Irony.Interpreter/LanguageRuntime/OperatorImplementation.cs @@ -0,0 +1,202 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Linq.Expressions; +using Irony.Parsing; + +namespace Irony.Interpreter { + + public delegate object UnaryOperatorMethod(object arg); + public delegate object BinaryOperatorMethod(object arg1, object arg2); + + #region OperatorDispatchKey class + /// + /// The struct is used as a key for the dictionary of operator implementations. + /// Contains types of arguments for a method or operator implementation. + /// + public struct OperatorDispatchKey { + public static readonly OperatorDispatchKeyComparer Comparer = new OperatorDispatchKeyComparer(); + public readonly ExpressionType Op; + public readonly Type Arg1Type; + public readonly Type Arg2Type; + public readonly int HashCode; + + //For binary operators + public OperatorDispatchKey(ExpressionType op, Type arg1Type, Type arg2Type) { + Op = op; + Arg1Type = arg1Type; + Arg2Type = arg2Type; + int h0 = (int)Op; + int h1 = Arg1Type.GetHashCode(); + int h2 = Arg2Type.GetHashCode(); + HashCode = unchecked(h0 << 8 ^ h1 << 4 ^ h2); + } + + //For unary operators + public OperatorDispatchKey(ExpressionType op, Type arg1Type) { + Op = op; + Arg1Type = arg1Type; + Arg2Type = null; + int h0 = (int)Op; + int h1 = Arg1Type.GetHashCode(); + int h2 = 0; + HashCode = unchecked(h0 << 8 ^ h1 << 4 ^ h2); + } + + public override int GetHashCode() { + return HashCode; + } + + public override string ToString() { + return Op + "(" + Arg1Type + ", " + Arg2Type + ")"; + } + }//class + #endregion + + #region OperatorDispatchKeyComparer class + // Note: I believe (guess) that a custom Comparer provided to a Dictionary is a bit more efficient + // than implementing IComparable on the key itself + public class OperatorDispatchKeyComparer : IEqualityComparer { + public bool Equals(OperatorDispatchKey x, OperatorDispatchKey y) { + return x.HashCode == y.HashCode && x.Op == y.Op && x.Arg1Type == y.Arg1Type && x.Arg2Type == y.Arg2Type; + } + public int GetHashCode(OperatorDispatchKey obj){ + return obj.HashCode; + } + }//class + #endregion + + public class TypeConverterTable : Dictionary { + public TypeConverterTable(int capacity) : base(capacity, OperatorDispatchKey.Comparer) {} + + }//class + + public class OperatorImplementationTable : Dictionary { + public OperatorImplementationTable(int capacity) : base(capacity, OperatorDispatchKey.Comparer) { } + } + + /// + ///The OperatorImplementation class represents an implementation of an operator for specific argument types. + /// + /// + /// The OperatorImplementation is used for holding implementation for binary operators, unary operators, + /// and type converters (special case of unary operators) + /// it holds 4 method references for binary operators: + /// converters for both arguments, implementation method and converter for the result. + /// For unary operators (and type converters) the implementation is in Arg1Converter + /// operator (arg1 is used); the converter method is stored in Arg1Converter; the target type is in CommonType + /// + public sealed class OperatorImplementation { + public readonly OperatorDispatchKey Key; + // The type to which arguments are converted and no-conversion method for this type. + public readonly Type CommonType; + public readonly BinaryOperatorMethod BaseBinaryMethod; + //converters + internal UnaryOperatorMethod Arg1Converter; + internal UnaryOperatorMethod Arg2Converter; + internal UnaryOperatorMethod ResultConverter; + //A reference to the actual binary evaluator method - one of EvaluateConvXXX + public BinaryOperatorMethod EvaluateBinary; + // An overflow handler - the implementation to handle arithmetic overflow + public OperatorImplementation OverflowHandler; + // No-box counterpart for implementations with auto-boxed output. If this field <> null, then this is + // implementation with auto-boxed output + public OperatorImplementation NoBoxImplementation; + + //Constructor for binary operators + public OperatorImplementation(OperatorDispatchKey key, Type resultType, BinaryOperatorMethod baseBinaryMethod, + UnaryOperatorMethod arg1Converter, UnaryOperatorMethod arg2Converter, UnaryOperatorMethod resultConverter) { + Key = key; + CommonType = resultType; + Arg1Converter = arg1Converter; + Arg2Converter = arg2Converter; + ResultConverter = resultConverter; + BaseBinaryMethod = baseBinaryMethod; + SetupEvaluationMethod(); + } + + //Constructor for unary operators and type converters + public OperatorImplementation(OperatorDispatchKey key, Type type, UnaryOperatorMethod method) { + Key = key; + CommonType = type; + Arg1Converter = method; + Arg2Converter = null; + ResultConverter = null; + BaseBinaryMethod = null; + } + + public override string ToString() { + return "[OpImpl for " + Key.ToString() + "]"; + } + + public void SetupEvaluationMethod() { + if (BaseBinaryMethod == null) + //special case - it is unary method, the method itself in Arg1Converter; LanguageRuntime.ExecuteUnaryOperator will handle this properly + return; + // Binary operator + if (ResultConverter == null) { + //without ResultConverter + if (Arg1Converter == null && Arg2Converter == null) + EvaluateBinary = EvaluateConvNone; + else if (Arg1Converter != null && Arg2Converter == null) + EvaluateBinary = EvaluateConvLeft; + else if (Arg1Converter == null && Arg2Converter != null) + EvaluateBinary = EvaluateConvRight; + else // if (Arg1Converter != null && arg2Converter != null) + EvaluateBinary = EvaluateConvBoth; + } else { + //with result converter + if (Arg1Converter == null && Arg2Converter == null) + EvaluateBinary = EvaluateConvNoneConvResult; + else if (Arg1Converter != null && Arg2Converter == null) + EvaluateBinary = EvaluateConvLeftConvResult; + else if (Arg1Converter == null && Arg2Converter != null) + EvaluateBinary = EvaluateConvRightConvResult; + else // if (Arg1Converter != null && Arg2Converter != null) + EvaluateBinary = EvaluateConvBothConvResult; + } + } + + private object EvaluateConvNone(object arg1, object arg2) { + return BaseBinaryMethod(arg1, arg2); + } + private object EvaluateConvLeft(object arg1, object arg2) { + return BaseBinaryMethod(Arg1Converter(arg1), arg2); + } + private object EvaluateConvRight(object arg1, object arg2) { + return BaseBinaryMethod(arg1, Arg2Converter(arg2)); + } + private object EvaluateConvBoth(object arg1, object arg2) { + return BaseBinaryMethod(Arg1Converter(arg1), Arg2Converter(arg2)); + } + + private object EvaluateConvNoneConvResult(object arg1, object arg2) { + return ResultConverter(BaseBinaryMethod(arg1, arg2)); + } + private object EvaluateConvLeftConvResult(object arg1, object arg2) { + return ResultConverter(BaseBinaryMethod(Arg1Converter(arg1), arg2)); + } + private object EvaluateConvRightConvResult(object arg1, object arg2) { + return ResultConverter(BaseBinaryMethod(arg1, Arg2Converter(arg2))); + } + private object EvaluateConvBothConvResult(object arg1, object arg2) { + return ResultConverter(BaseBinaryMethod(Arg1Converter(arg1), Arg2Converter(arg2))); + } + }//class + + + +}//namespace diff --git a/Irony.Interpreter/LanguageRuntime/SpecialFormsLibrary.cs b/Irony.Interpreter/LanguageRuntime/SpecialFormsLibrary.cs new file mode 100644 index 0000000..0c9aeb6 --- /dev/null +++ b/Irony.Interpreter/LanguageRuntime/SpecialFormsLibrary.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + public delegate object SpecialForm(ScriptThread thread, AstNode[] childNodes); + + public static class SpecialFormsLibrary { + public static object Iif(ScriptThread thread, AstNode[] childNodes) { + var testValue = childNodes[0].Evaluate(thread); + object result = thread.Runtime.IsTrue(testValue) ? childNodes[1].Evaluate(thread) : childNodes[2].Evaluate(thread); + return result; + + } + }//class +} diff --git a/Irony.Interpreter/Properties/AssemblyInfo.cs b/Irony.Interpreter/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..fcc7c11 --- /dev/null +++ b/Irony.Interpreter/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Irony.Interpreter")] +[assembly: AssemblyDescription("Irony Interpreter")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Irony Interpreter")] +[assembly: AssemblyCopyright("Copyright © Roman Ivantsov 2011")] +[assembly: AssemblyTrademark("Irony")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f2a09212-b2a0-4315-9e10-d6a5ee614fbe")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Irony.Interpreter/Scopes/AppDataMap.cs b/Irony.Interpreter/Scopes/AppDataMap.cs new file mode 100644 index 0000000..0a2de8e --- /dev/null +++ b/Irony.Interpreter/Scopes/AppDataMap.cs @@ -0,0 +1,50 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + /// Represents a set of all of static scopes/modules in the application. + public class AppDataMap { + public AstNode ProgramRoot; //artificial root associated with MainModule + public ScopeInfoList StaticScopeInfos = new ScopeInfoList(); + public ModuleInfoList Modules = new ModuleInfoList(); + public ModuleInfo MainModule; + public readonly bool LanguageCaseSensitive; + + public AppDataMap(bool languageCaseSensitive, AstNode programRoot = null) { + LanguageCaseSensitive = languageCaseSensitive; + ProgramRoot = programRoot?? new AstNode(); + var mainScopeInfo = new ScopeInfo(ProgramRoot, LanguageCaseSensitive); + StaticScopeInfos.Add(mainScopeInfo); + mainScopeInfo.StaticIndex = 0; + MainModule = new ModuleInfo("main", "main", mainScopeInfo); + Modules.Add(MainModule); + } + + public ModuleInfo GetModule(AstNode moduleNode) { + foreach (var m in Modules) + if (m.ScopeInfo == moduleNode.DependentScopeInfo) + return m; + return null; + } + + + }//class + +} diff --git a/Irony.Interpreter/Scopes/ModuleInfo.cs b/Irony.Interpreter/Scopes/ModuleInfo.cs new file mode 100644 index 0000000..2d3eccd --- /dev/null +++ b/Irony.Interpreter/Scopes/ModuleInfo.cs @@ -0,0 +1,41 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + public class ModuleInfoList : List { } + + public class ModuleInfo { + public readonly string Name; + public readonly string FileName; + public readonly ScopeInfo ScopeInfo; //scope for module variables + public readonly BindingSourceList Imports = new BindingSourceList(); + + public ModuleInfo(string name, string fileName, ScopeInfo scopeInfo) { + Name = name; + FileName = fileName; + ScopeInfo = scopeInfo; + } + + //Used for imported modules + public Binding BindToExport(BindingRequest request) { + return null; + } + + } +} diff --git a/Irony.Interpreter/Scopes/Scope.cs b/Irony.Interpreter/Scopes/Scope.cs new file mode 100644 index 0000000..0c53945 --- /dev/null +++ b/Irony.Interpreter/Scopes/Scope.cs @@ -0,0 +1,71 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + public class Scope : ScopeBase { + public object[] Parameters; + public Scope Caller; + public Scope Creator; //either caller or closure parent + private Scope _parent; //computed on demand + + public Scope(ScopeInfo scopeInfo, Scope caller, Scope creator, object[] parameters) : base(scopeInfo) { + Caller = caller; + Creator = creator; + Parameters = parameters; + } + + public object[] GetParameters() { + return Parameters; + } + + public object GetParameter(int index) { + return Parameters[index]; + } + public void SetParameter(int index, object value) { + Parameters[index] = value; + } + + // Lexical parent, computed on demand + public Scope Parent { + get { + if (_parent == null) + _parent = GetParent(); + return _parent; + } + set { _parent = value; } + } + + protected Scope GetParent() { + // Walk along creators chain and find a scope with ScopeInfo matching this.ScopeInfo.Parent + var parentScopeInfo = Info.Parent; + if (parentScopeInfo == null) + return null; + var current = Creator; + while (current != null) { + if (current.Info == parentScopeInfo) + return current; + current = current.Creator; + } + return null; + }// method + + }//class + +}//namespace diff --git a/Irony.Interpreter/Scopes/ScopeBase.cs b/Irony.Interpreter/Scopes/ScopeBase.cs new file mode 100644 index 0000000..99d461c --- /dev/null +++ b/Irony.Interpreter/Scopes/ScopeBase.cs @@ -0,0 +1,107 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace Irony.Interpreter { + + public class ScopeBase { + public ScopeInfo Info; + public volatile object[] Values; + + public ScopeBase(ScopeInfo scopeInfo) : this(scopeInfo, null) {} + public ScopeBase(ScopeInfo scopeInfo, object[] values) { + Info = scopeInfo; + Values = values; + if (Values == null) + Values = new object[scopeInfo.ValuesCount]; + } + + public SlotInfo AddSlot(string name) { + var slot = Info.AddSlot(name, SlotType.Value); + if (slot.Index >= Values.Length) + Resize(Values.Length + 4); + return slot; + } + + public object[] GetValues() { + return Values; + } + + public object GetValue(int index) { + try { + var tmp = Values; + // The following line may throw null-reference exception (tmp==null), if resizing is happening at the same time + // It may also throw IndexOutOfRange exception if new variable was added by another thread in another frame(scope) + // but this scope and Values array were created before that, so Values is shorter than #slots in SlotInfo. + // But in this case, it does not matter, result value is null (unassigned) + return tmp[index]; + } catch (NullReferenceException) { + Thread.Sleep(0); // Silverlight does not have Thread.Yield; + // Thread.Yield(); // maybe SpinWait.SpinOnce? + return GetValue(index); //repeat attempt + } catch (IndexOutOfRangeException) { + return null; //we do not resize here, value is unassigned anyway. + } + + }//method + + public void SetValue(int index, object value) { + try { + var tmp = Values; + // The following line may throw null-reference exception (tmp==null), if resizing is happening at the same time + // It may also throw IndexOutOfRange exception if new variable was added by another thread in another frame(scope) + // but this scope and Values array were created before that, so Values is shorter than #slots in SlotInfo + tmp[index] = value; + //Now check that tmp is the same as Values - if not, then resizing happened in the middle, + // so repeat assignment to make sure the value is in resized array. + if (tmp != Values) + SetValue(index, value); // do it again + } catch (NullReferenceException) { + Thread.Sleep(0); // it's OK to Sleep intead of SpinWait - it is really rare event, so we don't care losing a few more cycles here. + SetValue(index, value); //repeat it again + } catch (IndexOutOfRangeException) { + Resize(Info.GetSlotCount()); + SetValue(index, value); //repeat it again + } + }//method + + // Disabling warning: 'Values: a reference to a volatile field will not be treated as volatile' + // According to MSDN for CS0420 warning (see http://msdn.microsoft.com/en-us/library/4bw5ewxy.aspx), + // this does NOT apply to Interlocked API - which we use here. + #pragma warning disable 0420 + protected void Resize(int newSize) { + lock (this.Info.LockObject) { + if (Values.Length >= newSize) return; + object[] tmp = Interlocked.Exchange(ref Values, null); + Array.Resize(ref tmp, newSize); + Interlocked.Exchange(ref Values, tmp); + } + } + + public IDictionary AsDictionary() { + return new ScopeValuesDictionary(this); + } + + public override string ToString() { + return Info.ToString(); + } + + + }//class + + +} diff --git a/Irony.Interpreter/Scopes/ScopeInfo.cs b/Irony.Interpreter/Scopes/ScopeInfo.cs new file mode 100644 index 0000000..9a2eaa7 --- /dev/null +++ b/Irony.Interpreter/Scopes/ScopeInfo.cs @@ -0,0 +1,111 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + public class ScopeInfoList : List { } + + /// Describes all variables (locals and parameters) defined in a scope of a function or module. + /// ScopeInfo is metadata, it does not contain variable values. The Scope object (described by ScopeInfo) is a container for values. + // Note that all access to SlotTable is done through "lock" operator, so it's thread safe + public class ScopeInfo { + public int ValuesCount, ParametersCount; + public AstNode OwnerNode; //might be null + // Static/singleton scopes only; for ex, modules are singletons. Index in App.StaticScopes array + public int StaticIndex = -1; + public int Level; + public readonly string AsString; + public Scope ScopeInstance; //Experiment: reusable scope instance; see ScriptThread.cs class + + private SlotInfoDictionary _slots; + internal protected object LockObject = new object(); + + public ScopeInfo(AstNode ownerNode, bool caseSensitive) { + if (ownerNode == null) + throw new Exception("ScopeInfo owner node may not be null."); + OwnerNode = ownerNode; + _slots = new SlotInfoDictionary(caseSensitive); + Level = Parent == null ? 0 : Parent.Level + 1; + var sLevel = "level=" + Level; + AsString = OwnerNode == null ? sLevel : OwnerNode.AsString + ", " + sLevel; + + } + + //Lexical parent + public ScopeInfo Parent { + get { + if (_parent == null) + _parent = GetParent(); + return _parent; + } + } ScopeInfo _parent; + + public ScopeInfo GetParent() { + if (OwnerNode == null) return null; + var currentParent = OwnerNode.Parent; + while (currentParent != null) { + var result = currentParent.DependentScopeInfo; + if (result != null) return result; + currentParent = currentParent.Parent; + } + return null; //should never happen + } + + #region Slot operations + public SlotInfo AddSlot(string name, SlotType type) { + lock (LockObject) { + var index = type == SlotType.Value ? ValuesCount++ : ParametersCount++; + var slot = new SlotInfo(this, type, name, index); + _slots.Add(name, slot); + return slot; + } + } + + //Returns null if slot not found. + public SlotInfo GetSlot(string name) { + lock (LockObject) { + SlotInfo slot; + _slots.TryGetValue(name, out slot); + return slot; + } + } + + public IList GetSlots() { + lock (LockObject) { + return new List(_slots.Values); + } + } + + public IList GetNames() { + lock (LockObject) { + return new List(_slots.Keys); + } + } + + public int GetSlotCount() { + lock (LockObject) { + return _slots.Count; + } + } + #endregion + + public override string ToString() { + return AsString; + } + }//class +} //namespace diff --git a/Irony.Interpreter/Scopes/ScopeValuesDictionary.cs b/Irony.Interpreter/Scopes/ScopeValuesDictionary.cs new file mode 100644 index 0000000..e77baa1 --- /dev/null +++ b/Irony.Interpreter/Scopes/ScopeValuesDictionary.cs @@ -0,0 +1,115 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter { + /// + /// A wrapper around Scope exposing it as a string-object dictionary. Used to expose Globals dictionary from Main scope + /// + public class ScopeValuesDictionary : IDictionary { + ScopeBase _scope; + + internal ScopeValuesDictionary(ScopeBase scope) { + _scope = scope; + } + + public void Add(string key, object value) { + var slot = _scope.Info.GetSlot(key); + if (slot == null) + slot = _scope.AddSlot(key); + _scope.SetValue(slot.Index, value); + } + + public bool ContainsKey(string key) { + return _scope.Info.GetSlot(key) != null; + } + + public ICollection Keys { + get { return _scope.Info.GetNames(); } + } + + //We do not remove the slotInfo (you can't do that, slot set can only grow); instead we set the value to null + // to indicate "unassigned" + public bool Remove(string key) { + this[key] = null; + return true; + } + + public bool TryGetValue(string key, out object value) { + value = null; + SlotInfo slot = _scope.Info.GetSlot(key); + if (slot == null) + return false; + value = _scope.GetValue(slot.Index); + return true; + } + + public ICollection Values { + get {return _scope.GetValues(); } + } + + public object this[string key] { + get { + object value; + TryGetValue(key, out value); + return value; + } + set { + Add(key, value); + } + } + + public void Add(KeyValuePair item) { + Add(item.Key, item.Value); + } + + public void Clear() { + var values = _scope.GetValues(); + for (int i = 0; i < values.Length; i++) + values[i] = null; + } + + public bool Contains(KeyValuePair item) { + return _scope.Info.GetSlot(item.Key) != null; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) { + throw new NotImplementedException(); + } + + public int Count { + get { return _scope.Info.GetSlotCount(); } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(KeyValuePair item) { + return Remove(item.Key); + } + + public IEnumerator> GetEnumerator() { + var slots = _scope.Info.GetSlots(); //make local copy + foreach (var slot in slots) + yield return new KeyValuePair(slot.Name, _scope.GetValue(slot.Index)); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + } +} diff --git a/Irony.Interpreter/Scopes/SlotInfo.cs b/Irony.Interpreter/Scopes/SlotInfo.cs new file mode 100644 index 0000000..8599d02 --- /dev/null +++ b/Irony.Interpreter/Scopes/SlotInfo.cs @@ -0,0 +1,47 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter { + + public enum SlotType { + Value, //local or property value + Parameter, //function parameter + Function, + Closure, + } + + /// Describes a variable. + public class SlotInfo { + public readonly ScopeInfo ScopeInfo; + public readonly SlotType Type; + public readonly string Name; + public readonly int Index; + public bool IsPublic = true; //for module-level slots, indicator that symbol is "exported" and visible by code that imports the module + internal SlotInfo(ScopeInfo scopeInfo, SlotType type, string name, int index) { + ScopeInfo = scopeInfo; + Type = type; + Name = name; + Index = index; + } + } + + public class SlotInfoDictionary : Dictionary { + public SlotInfoDictionary(bool caseSensitive) + : base(32, caseSensitive ? StringComparer.InvariantCulture : StringComparer.InvariantCultureIgnoreCase) { } + } + +} diff --git a/Irony.Interpreter/Scopes/_about_storage.txt b/Irony.Interpreter/Scopes/_about_storage.txt new file mode 100644 index 0000000..b992390 --- /dev/null +++ b/Irony.Interpreter/Scopes/_about_storage.txt @@ -0,0 +1,84 @@ +Variables/values storage - some traditonal approaches + +1. In scripting languages, the data elements (fields, properties, local variables) are created on the fly, on the first write. There's no pre-allocation at compile time. The set of variables is unknown in advance. Traditional, straightforward solution is to use a dictionary (string=>object) to store local variables of a function, or module-level variables. +2. In free-threaded environment the variables may be accessed from different threads. This means that the access to containing dictionaries should be performed using thread-locking mechanism, guaanteeing that only a single thread is accessing a dictionary. +So the script statement like: + + x = 5 (1) + +is translated into something like this: + + lock(scope) { (2) + scope.Values["x"] = 5; + } + +Two important and unfortunate observations. +1. Dictionary access is slow. At least if we mean .NET Dictionary generic class. Simple tests coupled with source code inspection show that the cost is in the range of hundreds of processor instructions. +2. Thread locking is slow. The cost is also in the range of hundreds of instructions. + +The result is that implementation (2) is quite slow. Really slow, especially considering the fact that actual thread collisions on the same dictionary objects are quite rare, while we have to incur the extra cost of lock every time we read or write a value. + +What can be done better? +Our interpreter stores data in linear arrays, and does NOT use thread locking when reading/writing the data. There is an explicit locking when we "create" a variable for the first time - we lock meta-data dictionary containing "descriptions" of data slots; but then all subsequent accesses to the value are done performed using the variable index. +But before we explain how it works, we need to state one explicit assumption we rely on: + + Assumption: + Assignment of an object reference to a variable (ex: x = someObj) is an atomic operation and is "thread-safe". + +So the assignment can be safely done without thread locking. if one thread makes an assignment, and the other thread reads the reference, this other thread would see either old or new value, but never any "corrupted middle". This assumption mostly concerns safety of Reading from another thread. A special case is writing or replacing the value (when we resize the Values array, we replace it with new resized copy) - see more on this below. + +Back to Irony's data storage implementation: arrays with no-lock read/write access. The data is stored in linear array of objects: Values[] field (see ScopeBase class). The field is marked with "volatile" keyword. All access is done by index. + +Let's look at an example and explain what happens. Suppose we have an AST node that represents a variable "x" with READ access. When interpreter evaluates this node for the first time, it looks up a variable metadata (SlotInfo) in current scope metadata (ScopeInfo). The result is linear index of the data value in Values array. It then reads the value using the index: + + vx = scope.Values[xSlotIndex]; + +All later evaluations will do the same - lookup by index but without looking up the SlotInfo: the xSlotIndex is cached in the node (more accurately, in SlotBinding object). Writing the value works the same way - the array element is assigned by index. +The problems comes when we need to resize the Values array because we are adding some local variable - for example, our script runs into new assignment statement in the local scope: + + y = 5 + +We need to add "y" to the list of slots (metadata), but then we also need to "extend" the Values[] array and add an extra element for "y". The question now is: how to resize Values array in such a way that if some other thread(s) is reading or writing other values in the same scope, it does it correctly even if it happens exactly at the moment when we resize the array? +Here's how we do it. First let's look at the ScopeBase.Resize method: + + #pragma warning disable 0420 + protected void Resize(int newSize) { + lock (this) { + if (Values.Length >= newSize) return; + object[] tmp = Interlocked.Exchange(ref Values, null); + Array.Resize(ref tmp, newSize); + Interlocked.Exchange(ref Values, tmp); + } + } + +We use Interlocked.Exchange to replace Values field with null as an atomic operation. We do it to force any concurrent reads/writes to fail, if they happen at exactly this time. Note that we disable a compiler warning stating that volatile field Values will not be treated as volatile in a call to Interlocked.Exchange. According to MSDN, this is usually the case with "by-ref" arguments, but Interlocked API is an exception, so we're OK here. +Now, in GetValue and SetValue methods, we expect this failure, and have a try/catch block to handle the null reference exception and retry the operation. Here's SetValues method: + + public void SetValue(int index, object value) { + try { + var tmp = Values; + tmp[index] = value; + //Now check that tmp is the same as Values - if not, then resizing happened in the middle, + // so repeat assignment to make sure the value is in resized array. + if (tmp != Values) + SetValue(index, value); // do it again + } catch (NullReferenceException) { + Thread.Sleep(0); + SetValue(index, value); //repeat it again + } ..... + }//method + + The "catch" block for NullReferenceException is for handling the situation when Values was null while other thread was resizing it. Remember that try/catch block is free, it does not add any executable commands if we run without exception. +There is additional twist when writing a value. It might happen that after we copied the Values reference into tmp variable, some other thread resized the Values array - replacing it with a new extended array. As a result, we will be writing the value into a "dead" old array. To check against this, after we do the value change we check that "tmp" and "Values" reference the same object. If not, we got concurrent resize, so we repeat SetValue to make sure we set it in the new Values instance. + +To sum it up: all variables are stored in object arrays, values are accessed by index, and accessed without explicit thread locks. The net result of this technique (and some other improvements) is approximate 5-fold performance gain - compared to old interpreter. + +NOW 5 TIMES FASTER! + +References: +Very illuminating article about low-lock memory access: +http://msdn.microsoft.com/en-us/magazine/cc163715.aspx + +Another article about spin locks and interlocked operations: +http://msdn.microsoft.com/en-us/magazine/cc163715.aspx + diff --git a/Irony.Interpreter/SriptApplication/CommandLine.cs b/Irony.Interpreter/SriptApplication/CommandLine.cs new file mode 100644 index 0000000..5d62802 --- /dev/null +++ b/Irony.Interpreter/SriptApplication/CommandLine.cs @@ -0,0 +1,197 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Diagnostics; +using Irony.Parsing; + +namespace Irony.Interpreter { + + //An abstraction of a Console. + public interface IConsoleAdaptor { + bool Canceled { get; set; } + void Write(string text); + void WriteLine(string text); + void SetTextStyle(ConsoleTextStyle style); + int Read(); //reads a key + string ReadLine(); //reads a line; returns null if Ctrl-C is pressed + void SetTitle(string title); + } + + //WARNING: Ctrl-C for aborting running script does NOT work when you run console app from Visual Studio 2010. + // Run executable directly from bin folder. + public class CommandLine { + #region Fields and properties + public readonly LanguageRuntime Runtime; + public readonly IConsoleAdaptor _console; + //Initialized from grammar + public string Title; + public string Greeting; + public string Prompt; //default prompt + public string PromptMoreInput; //prompt to show when more input is expected + + public readonly ScriptApp App; + Thread _workerThread; + public bool IsEvaluating { get; private set; } + + #endregion + + public CommandLine(LanguageRuntime runtime, IConsoleAdaptor console = null) { + Runtime = runtime; + _console = console ?? new ConsoleAdapter(); + var grammar = runtime.Language.Grammar; + Title = grammar.ConsoleTitle; + Greeting = grammar.ConsoleGreeting; + Prompt = grammar.ConsolePrompt; + PromptMoreInput = grammar.ConsolePromptMoreInput; + App = new ScriptApp(Runtime); + App.ParserMode = ParseMode.CommandLine; + // App.PrintParseErrors = false; + App.RethrowExceptions = false; + + } + + public void Run() { + try { + RunImpl(); + } catch (Exception ex) { + _console.SetTextStyle(ConsoleTextStyle.Error); + _console.WriteLine(Resources.ErrConsoleFatalError); + _console.WriteLine(ex.ToString()); + _console.SetTextStyle(ConsoleTextStyle.Normal); + _console.WriteLine(Resources.MsgPressAnyKeyToExit); + _console.Read(); + } + } + + + private void RunImpl() { + + _console.SetTitle(Title); + _console.WriteLine(Greeting); + string input; + while (true) { + _console.Canceled = false; + _console.SetTextStyle(ConsoleTextStyle.Normal); + string prompt = (App.Status == AppStatus.WaitingMoreInput ? PromptMoreInput : Prompt); + + //Write prompt, read input, check for Ctrl-C + _console.Write(prompt); + input = _console.ReadLine(); + if (_console.Canceled) + if (Confirm(Resources.MsgExitConsoleYN)) + return; + else + continue; //from the start of the loop + + //Execute + App.ClearOutputBuffer(); + EvaluateAsync(input); + //Evaluate(input); + WaitForScriptComplete(); + + switch (App.Status) { + case AppStatus.Ready: //success + _console.WriteLine(App.GetOutput()); + break; + case AppStatus.SyntaxError: + _console.WriteLine(App.GetOutput()); //write all output we have + _console.SetTextStyle(ConsoleTextStyle.Error); + foreach (var err in App.GetParserMessages()) { + _console.WriteLine(string.Empty.PadRight(prompt.Length + err.Location.Column) + "^"); //show err location + _console.WriteLine(err.Message); //print message + } + break; + case AppStatus.Crash: + case AppStatus.RuntimeError: + ReportException(); + break; + default: break; + }//switch + } + + }//Run method + + private void WaitForScriptComplete() { + _console.Canceled = false; + while(true) { + Thread.Sleep(50); + if(!IsEvaluating) return; + if(_console.Canceled) { + _console.Canceled = false; + if (Confirm(Resources.MsgAbortScriptYN)) + WorkerThreadAbort(); + }//if Canceled + } + } + + private void Evaluate(string script) { + try { + IsEvaluating = true; + App.Evaluate(script); + } finally { + IsEvaluating = false; + } + } + + private void EvaluateAsync(string script) { + IsEvaluating = true; + _workerThread = new Thread(WorkerThreadStart); + _workerThread.Start(script); + } + + private void WorkerThreadStart(object data) { + try { + var script = data as string; + App.Evaluate(script); + } finally { + IsEvaluating = false; + } + } + private void WorkerThreadAbort() { + try { + _workerThread.Abort(); + _workerThread.Join(50); + } finally { + IsEvaluating = false; + } + } + + private bool Confirm(string message) { + _console.WriteLine(string.Empty); + _console.Write(message); + var input = _console.ReadLine(); + return Resources.ConsoleYesChars.Contains(input); + } + + private void ReportException() { + _console.SetTextStyle(ConsoleTextStyle.Error); + var ex = App.LastException; + var scriptEx = ex as ScriptException; + if (scriptEx != null) + _console.WriteLine(scriptEx.Message + " " + Resources.LabelLocation + " " + scriptEx.Location.ToUiString()); + else { + if (App.Status == AppStatus.Crash) + _console.WriteLine(ex.ToString()); //Unexpected interpreter crash: the full stack when debugging your language + else + _console.WriteLine(ex.Message); + + } + // + } + + }//class +} diff --git a/Irony.Interpreter/SriptApplication/ConsoleAdaptor.cs b/Irony.Interpreter/SriptApplication/ConsoleAdaptor.cs new file mode 100644 index 0000000..0e2420a --- /dev/null +++ b/Irony.Interpreter/SriptApplication/ConsoleAdaptor.cs @@ -0,0 +1,74 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Irony.Parsing; + +namespace Irony.Interpreter { + //WARNING: Ctrl-C for aborting running script does NOT work when you run console app from Visual Studio 2010. + // Run executable directly from bin folder. + + public enum ConsoleTextStyle { + Normal, + Error, + } + + // Default implementation of IConsoleAdaptor with System Console as input/output. + public class ConsoleAdapter : IConsoleAdaptor { + public ConsoleAdapter() { + Console.CancelKeyPress += Console_CancelKeyPress; + } + + void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; //do not kill the app yet + Canceled = true; + } + + public bool Canceled { get; set; } + + public void Write(string text) { + Console.Write(text); + } + public void WriteLine(string text) { + Console.WriteLine(text); + } + public void SetTextStyle(ConsoleTextStyle style) { + switch(style) { + case ConsoleTextStyle.Normal: + Console.ForegroundColor = ConsoleColor.White; + break; + case ConsoleTextStyle.Error: + Console.ForegroundColor = ConsoleColor.Red; + break; + } + } + + public int Read() { + return Console.Read(); + } + + public string ReadLine() { + var input = Console.ReadLine(); + Canceled = (input == null); // Windows console method ReadLine returns null if Ctrl-C was pressed. + return input; + } + public void SetTitle(string title) { + Console.Title = title; + } + } + + +} diff --git a/Irony.Interpreter/SriptApplication/ScriptApp.cs b/Irony.Interpreter/SriptApplication/ScriptApp.cs new file mode 100644 index 0000000..2196135 --- /dev/null +++ b/Irony.Interpreter/SriptApplication/ScriptApp.cs @@ -0,0 +1,231 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.Security; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + + public enum AppStatus { + Ready, + Evaluating, + WaitingMoreInput, //command line only + SyntaxError, + RuntimeError, + Crash, //interpreter crash + Aborted + } + + /// Represents a running instance of a script application. + public sealed class ScriptApp { + public readonly LanguageData Language; + public readonly LanguageRuntime Runtime; + public Parser Parser { get; private set; } + + public AppDataMap DataMap; + + public Scope[] StaticScopes; + public Scope MainScope; + public IDictionary Globals {get; private set;} + private IList ImportedAssemblies = new List(); + + public StringBuilder OutputBuffer = new StringBuilder(); + private object _lockObject = new object(); + + // Current mode/status variables + public AppStatus Status; + public long EvaluationTime; + public Exception LastException; + public bool RethrowExceptions = true; + + public ParseTree LastScript { get; private set; } //the root node of the last executed script + + + #region Constructors + public ScriptApp(LanguageData language) { + Language = language; + var grammar = language.Grammar as InterpretedLanguageGrammar; + Runtime = grammar.CreateRuntime(language); + DataMap = new AppDataMap(Language.Grammar.CaseSensitive); + Init(); + } + + public ScriptApp(LanguageRuntime runtime) { + Runtime = runtime; + Language = Runtime.Language; + DataMap = new AppDataMap(Language.Grammar.CaseSensitive); + Init(); + } + + public ScriptApp(AppDataMap dataMap) { + DataMap = dataMap; + Init(); + } + + [SecuritySafeCritical] + private void Init() { + Parser = new Parser(Language); + //Create static scopes + MainScope = new Scope(DataMap.MainModule.ScopeInfo, null, null, null); + StaticScopes = new Scope[DataMap.StaticScopeInfos.Count]; + StaticScopes[0] = MainScope; + Globals = MainScope.AsDictionary(); + } + + #endregion + + public LogMessageList GetParserMessages() { + return Parser.Context.CurrentParseTree.ParserMessages; + } + // Utilities + public IEnumerable GetImportAssemblies() { + //simple default case - return all assemblies loaded in domain + return AppDomain.CurrentDomain.GetAssemblies(); + } + + public ParseMode ParserMode { + get { return Parser.Context.Mode; } + set { Parser.Context.Mode = value; } + } + + #region Evaluation + public object Evaluate(string script) { + try { + var parsedScript = Parser.Parse(script); + if (parsedScript.HasErrors()) { + Status = AppStatus.SyntaxError; + if (RethrowExceptions) + throw new ScriptException("Syntax errors found."); + return null; + } + + if (ParserMode == ParseMode.CommandLine && Parser.Context.Status == ParserStatus.AcceptedPartial) { + Status = AppStatus.WaitingMoreInput; + return null; + } + LastScript = parsedScript; + var result = EvaluateParsedScript(); + return result; + } catch (ScriptException) { + throw; + } catch (Exception ex) { + this.LastException = ex; + this.Status = AppStatus.Crash; + return null; + } + } + + // Irony interpreter requires that once a script is executed in a ScriptApp, it is bound to AppDataMap object, + // and all later script executions should be performed only in the context of the same app (or at least by an App with the same DataMap). + // The reason is because the first execution sets up a data-binding fields, like slots, scopes, etc, which are bound to ScopeInfo objects, + // which in turn is part of DataMap. + public object Evaluate(ParseTree parsedScript) { + Util.Check (parsedScript.Root.AstNode != null, "Root AST node is null, cannot evaluate script. Create AST tree first."); + var root = parsedScript.Root.AstNode as AstNode; + Util.Check(root != null, + "Root AST node {0} is not a subclass of Irony.Interpreter.AstNode. ScriptApp cannot evaluate this script.", root.GetType()); + Util.Check (root.Parent == null || root.Parent == DataMap.ProgramRoot, + "Cannot evaluate parsed script. It had been already evaluated in a different application."); + LastScript = parsedScript; + return EvaluateParsedScript(); + } + + public object Evaluate() { + Util.Check (LastScript != null, "No previously parsed/evaluated script."); + return EvaluateParsedScript(); + } + + //Actual implementation + private object EvaluateParsedScript() { + LastScript.Tag = DataMap; + var root = LastScript.Root.AstNode as AstNode; + root.DependentScopeInfo = MainScope.Info; + + Status = AppStatus.Evaluating; + ScriptThread thread = null; + try { + thread = new ScriptThread(this); + var result = root.Evaluate(thread); + if (result != null) + thread.App.WriteLine(result.ToString()); + Status = AppStatus.Ready; + return result; + } catch (ScriptException se) { + Status = AppStatus.RuntimeError; + se.Location = thread.CurrentNode.Location; + se.ScriptStackTrace = thread.GetStackTrace(); + LastException = se; + if (RethrowExceptions) + throw; + return null; + } catch (Exception ex) { + Status = AppStatus.RuntimeError; + var se = new ScriptException(ex.Message, ex, thread.CurrentNode.Location, thread.GetStackTrace()); + LastException = se; + if (RethrowExceptions) + throw se; + return null; + + }//catch + + } + #endregion + + + #region Output writing + #region ConsoleWrite event + public event EventHandler ConsoleWrite; + private void OnConsoleWrite(string text) { + if (ConsoleWrite != null) { + ConsoleWriteEventArgs args = new ConsoleWriteEventArgs(text); + ConsoleWrite(this, args); + } + } + #endregion + + + + public void Write(string text) { + lock(_lockObject){ + OnConsoleWrite(text); + OutputBuffer.Append(text); + } + } + public void WriteLine(string text) { + lock(_lockObject){ + OnConsoleWrite(text + Environment.NewLine); + OutputBuffer.AppendLine(text); + } + } + + public void ClearOutputBuffer() { + lock(_lockObject){ + OutputBuffer.Clear(); + } + } + + public string GetOutput() { + lock(_lockObject){ + return OutputBuffer.ToString(); + } + } + #endregion + + + + }//class +} diff --git a/Irony.Interpreter/SriptApplication/ScriptThread.cs b/Irony.Interpreter/SriptApplication/ScriptThread.cs new file mode 100644 index 0000000..75cbaa9 --- /dev/null +++ b/Irony.Interpreter/SriptApplication/ScriptThread.cs @@ -0,0 +1,100 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + /// Represents a running thread in script application. + public sealed class ScriptThread : IBindingSource { + public readonly ScriptApp App; + public readonly LanguageRuntime Runtime; + + public Scope CurrentScope; + public AstNode CurrentNode; + + // Tail call parameters + public ICallTarget Tail; + public object[] TailArgs; + + public ScriptThread(ScriptApp app) { + App = app; + Runtime = App.Runtime; + CurrentScope = app.MainScope; + } + + public void PushScope(ScopeInfo scopeInfo, object[] parameters) { + CurrentScope = new Scope(scopeInfo, CurrentScope, CurrentScope, parameters); + } + + + + public void PushClosureScope(ScopeInfo scopeInfo, Scope closureParent, object[] parameters) { + CurrentScope = new Scope(scopeInfo, CurrentScope, closureParent, parameters); + } + + public void PopScope() { + CurrentScope = CurrentScope.Caller; + } + + public Binding Bind(string symbol, BindingRequestFlags options) { + var request = new BindingRequest(this, CurrentNode, symbol, options); + var binding = Bind(request); + if (binding == null) + ThrowScriptError("Unknown symbol '{0}'.", symbol); + return binding; + } + + #region Exception handling + public object HandleError(Exception exception) { + if (exception is ScriptException) + throw exception; + var stack = GetStackTrace(); + var rex = new ScriptException(exception.Message, exception, CurrentNode.ErrorAnchor, stack); + throw rex; + } + + // Throws ScriptException exception. + public void ThrowScriptError(string message, params object[] args) { + if (args != null && args.Length > 0) + message = string.Format(message, args); + var loc = GetCurrentLocation(); + var stack = GetStackTrace(); + throw new ScriptException(message, null, loc, stack); + } + + //TODO: add construction of Script Call stack + public ScriptStackTrace GetStackTrace() { + return new ScriptStackTrace(); + } + + private SourceLocation GetCurrentLocation() { + return this.CurrentNode == null ? new SourceLocation() : CurrentNode.Location; + } + + #endregion + + + #region IBindingSource Members + + public Binding Bind(BindingRequest request) { + return Runtime.Bind(request); + } + + #endregion + }//class +} diff --git a/Irony.Interpreter/Utilities/Extensions.cs b/Irony.Interpreter/Utilities/Extensions.cs new file mode 100644 index 0000000..a048589 --- /dev/null +++ b/Irony.Interpreter/Utilities/Extensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter { + public static class InterpreterEnumExtensions { + + public static bool IsSet(this BindingRequestFlags enumValue, BindingRequestFlags flag) { + return (enumValue & flag) != 0; + } + public static bool IsSet(this AstNodeFlags enumValue, AstNodeFlags flag) { + return (enumValue & flag) != 0; + } + + } + + +} diff --git a/Irony.Interpreter/Utilities/Util.cs b/Irony.Interpreter/Utilities/Util.cs new file mode 100644 index 0000000..4efd1ce --- /dev/null +++ b/Irony.Interpreter/Utilities/Util.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Interpreter { + public static class Util { + public static string SafeFormat(this string template, params object[] args) { + if (args == null || args.Length == 0) return template; + try { + template = string.Format(template, args); + } catch (Exception ex) { + template = template + "(message formatting failed: " + ex.Message + " Args: " + string.Join(",", args) + ")"; + } + return template; + }//method + + public static void Check(bool condition, string messageTemplate, params object[] args) { + if (condition) return; + throw new Exception(messageTemplate.SafeFormat(args)); + } + + }//class +} diff --git a/Irony.Interpreter/_Evaluator/ExpressionEvaluator.cs b/Irony.Interpreter/_Evaluator/ExpressionEvaluator.cs new file mode 100644 index 0000000..36fc585 --- /dev/null +++ b/Irony.Interpreter/_Evaluator/ExpressionEvaluator.cs @@ -0,0 +1,69 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; +using System.Threading; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter.Evaluator { + public class ExpressionEvaluator { + public ExpressionEvaluatorGrammar Grammar {get; private set;} + public Parser Parser {get; private set;} + public LanguageData Language {get; private set;} + public LanguageRuntime Runtime {get; private set;} + public ScriptApp App {get; private set;} + + public IDictionary Globals { + get { return App.Globals; } + } + + //Default constructor, creates default evaluator + public ExpressionEvaluator() : this(new ExpressionEvaluatorGrammar()) { + } + + //Default constructor, creates default evaluator + public ExpressionEvaluator(ExpressionEvaluatorGrammar grammar) { + Grammar = grammar; + Language = new LanguageData(Grammar); + Parser = new Parser(Language); + Runtime = Grammar.CreateRuntime(Language); + App = new ScriptApp(Runtime); + } + + public object Evaluate(string script) { + var result = App.Evaluate(script); + return result; + } + + public object Evaluate(ParseTree parsedScript) { + var result = App.Evaluate(parsedScript); + return result; + } + + //Evaluates again the previously parsed/evaluated script + public object Evaluate() { + return App.Evaluate(); + } + + public void ClearOutput() { + App.ClearOutputBuffer(); + } + public string GetOutput() { + return App.GetOutput(); + } + + }//class +} diff --git a/Irony.Interpreter/_Evaluator/ExpressionEvaluatorGrammar.cs b/Irony.Interpreter/_Evaluator/ExpressionEvaluatorGrammar.cs new file mode 100644 index 0000000..1b0eac0 --- /dev/null +++ b/Irony.Interpreter/_Evaluator/ExpressionEvaluatorGrammar.cs @@ -0,0 +1,182 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; +using Irony.Interpreter; +using Irony.Interpreter.Ast; + +namespace Irony.Interpreter.Evaluator { + // A ready-to-use evaluator implementation. + + // This grammar describes programs that consist of simple expressions and assignments + // for ex: + // x = 3 + // y = -x + 5 + // the result of calculation is the result of last expression or assignment. + // Irony's default runtime provides expression evaluation. + // supports inc/dec operators (++,--), both prefix and postfix, and combined assignment operators like +=, -=, etc. + // supports bool operators &, |, and short-circuit versions &&, || + // supports ternary ?: operator + + [Language("ExpressionEvaluator", "1.0", "Multi-line expression evaluator")] + public class ExpressionEvaluatorGrammar : InterpretedLanguageGrammar { + public ExpressionEvaluatorGrammar() : base(caseSensitive: false) { + this.GrammarComments = +@"Irony expression evaluator. Case-insensitive. Supports big integers, float data types, variables, assignments, +arithmetic operations, augmented assignments (+=, -=), inc/dec (++,--), strings with embedded expressions; +bool operations &,&&, |, ||; ternary '?:' operator." ; + // 1. Terminals + var number = new NumberLiteral("number"); + //Let's allow big integers (with unlimited number of digits): + number.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt }; + var identifier = new IdentifierTerminal("identifier"); + var comment = new CommentTerminal("comment", "#", "\n", "\r"); + //comment must be added to NonGrammarTerminals list; it is not used directly in grammar rules, + // so we add it to this list to let Scanner know that it is also a valid terminal. + base.NonGrammarTerminals.Add(comment); + var comma = ToTerm(","); + + //String literal with embedded expressions ------------------------------------------------------------------ + var stringLit = new StringLiteral("string", "\"", StringOptions.AllowsAllEscapes | StringOptions.IsTemplate); + stringLit.AddStartEnd("'", StringOptions.AllowsAllEscapes | StringOptions.IsTemplate); + stringLit.AstConfig.NodeType = typeof(StringTemplateNode); + var Expr = new NonTerminal("Expr"); //declare it here to use in template definition + var templateSettings = new StringTemplateSettings(); //by default set to Ruby-style settings + templateSettings.ExpressionRoot = Expr; //this defines how to evaluate expressions inside template + this.SnippetRoots.Add(Expr); + stringLit.AstConfig.Data = templateSettings; + //-------------------------------------------------------------------------------------------------------- + + // 2. Non-terminals + var Term = new NonTerminal("Term"); + var BinExpr = new NonTerminal("BinExpr", typeof(BinaryOperationNode)); + var ParExpr = new NonTerminal("ParExpr"); + var UnExpr = new NonTerminal("UnExpr", typeof(UnaryOperationNode)); + var TernaryIfExpr = new NonTerminal("TernaryIf", typeof(IfNode)); + var ArgList = new NonTerminal("ArgList", typeof(ExpressionListNode)); + var FunctionCall = new NonTerminal("FunctionCall", typeof(FunctionCallNode)); + var MemberAccess = new NonTerminal("MemberAccess", typeof(MemberAccessNode)); + var IndexedAccess = new NonTerminal("IndexedAccess", typeof(IndexedAccessNode)); + var ObjectRef = new NonTerminal("ObjectRef"); // foo, foo.bar or f['bar'] + var UnOp = new NonTerminal("UnOp"); + var BinOp = new NonTerminal("BinOp", "operator"); + var PrefixIncDec = new NonTerminal("PrefixIncDec", typeof(IncDecNode)); + var PostfixIncDec = new NonTerminal("PostfixIncDec", typeof(IncDecNode)); + var IncDecOp = new NonTerminal("IncDecOp"); + var AssignmentStmt = new NonTerminal("AssignmentStmt", typeof(AssignmentNode)); + var AssignmentOp = new NonTerminal("AssignmentOp", "assignment operator"); + var Statement = new NonTerminal("Statement"); + var Program = new NonTerminal("Program", typeof(StatementListNode)); + + // 3. BNF rules + Expr.Rule = Term | UnExpr | BinExpr | PrefixIncDec | PostfixIncDec | TernaryIfExpr; + Term.Rule = number | ParExpr | stringLit | FunctionCall | identifier | MemberAccess | IndexedAccess; + ParExpr.Rule = "(" + Expr + ")"; + UnExpr.Rule = UnOp + Term + ReduceHere(); + UnOp.Rule = ToTerm("+") | "-"; + BinExpr.Rule = Expr + BinOp + Expr; + BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "**" | "==" | "<" | "<=" | ">" | ">=" | "!=" | "&&" | "||" | "&" | "|"; + PrefixIncDec.Rule = IncDecOp + identifier; + PostfixIncDec.Rule = identifier + PreferShiftHere() + IncDecOp; + IncDecOp.Rule = ToTerm("++") | "--"; + TernaryIfExpr.Rule = Expr + "?" + Expr + ":" + Expr; + MemberAccess.Rule = Expr + PreferShiftHere() + "." + identifier; + AssignmentStmt.Rule = ObjectRef + AssignmentOp + Expr; + AssignmentOp.Rule = ToTerm("=") | "+=" | "-=" | "*=" | "/="; + Statement.Rule = AssignmentStmt | Expr | Empty; + ArgList.Rule = MakeStarRule(ArgList, comma, Expr); + FunctionCall.Rule = Expr + PreferShiftHere() + "(" + ArgList + ")"; + FunctionCall.NodeCaptionTemplate = "call #{0}(...)"; + ObjectRef.Rule = identifier | MemberAccess | IndexedAccess; + IndexedAccess.Rule = Expr + PreferShiftHere() + "[" + Expr + "]"; + + Program.Rule = MakePlusRule(Program, NewLine, Statement); + + this.Root = Program; // Set grammar root + + // 4. Operators precedence + RegisterOperators(10, "?"); + RegisterOperators(15, "&", "&&", "|", "||"); + RegisterOperators(20, "==", "<", "<=", ">", ">=", "!="); + RegisterOperators(30, "+", "-"); + RegisterOperators(40, "*", "/"); + RegisterOperators(50, Associativity.Right, "**"); + // For precedence to work, we need to take care of one more thing: BinOp. + //For BinOp which is or-combination of binary operators, we need to either + // 1) mark it transient or 2) set flag TermFlags.InheritPrecedence + // We use first option, making it Transient. + + // 5. Punctuation and transient terms + MarkPunctuation("(", ")", "?", ":", "[", "]"); + RegisterBracePair("(", ")"); + RegisterBracePair("[", "]"); + MarkTransient(Term, Expr, Statement, BinOp, UnOp, IncDecOp, AssignmentOp, ParExpr, ObjectRef); + + // 7. Syntax error reporting + MarkNotReported("++", "--"); + AddToNoReportGroup("(", "++", "--"); + AddToNoReportGroup(NewLine); + AddOperatorReportGroup("operator"); + AddTermsReportGroup("assignment operator", "=", "+=", "-=", "*=", "/="); + + //8. Console + ConsoleTitle = "Irony Expression Evaluator"; + ConsoleGreeting = +@"Irony Expression Evaluator + + Supports variable assignments, arithmetic operators (+, -, *, /), + augmented assignments (+=, -=, etc), prefix/postfix operators ++,--, string operations. + Supports big integer arithmetics, string operations. + Supports strings with embedded expressions : ""name: #{name}"" + +Press Ctrl-C to exit the program at any time. +"; + ConsolePrompt = "?"; + ConsolePromptMoreInput = "?"; + + //9. Language flags. + // Automatically add NewLine before EOF so that our BNF rules work correctly when there's no final line break in source + this.LanguageFlags = LanguageFlags.NewLineBeforeEOF | LanguageFlags.CreateAst | LanguageFlags.SupportsBigInt; + } + + public override LanguageRuntime CreateRuntime(LanguageData language) { + return new ExpressionEvaluatorRuntime(language); + } + + #region Running in Grammar Explorer + private static ExpressionEvaluator _evaluator; + public override string RunSample(RunSampleArgs args) { + if (_evaluator == null) { + _evaluator = new ExpressionEvaluator(this); + _evaluator.Globals.Add("null", _evaluator.Runtime.NoneValue); + _evaluator.Globals.Add("true", true); + _evaluator.Globals.Add("false", false); + + } + _evaluator.ClearOutput(); + //for (int i = 0; i < 1000; i++) //for perf measurements, to execute 1000 times + _evaluator.Evaluate(args.ParsedSample); + return _evaluator.GetOutput(); + } + #endregion + + + + + }//class + +}//namespace + + diff --git a/Irony.Interpreter/_Evaluator/ExpressionEvaluatorRuntime.cs b/Irony.Interpreter/_Evaluator/ExpressionEvaluatorRuntime.cs new file mode 100644 index 0000000..c7fe1c1 --- /dev/null +++ b/Irony.Interpreter/_Evaluator/ExpressionEvaluatorRuntime.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Interpreter.Evaluator { + public class ExpressionEvaluatorRuntime : LanguageRuntime { + public ExpressionEvaluatorRuntime(LanguageData language) : base(language) { + } + public override void Init() { + base.Init(); + //add built-in methods, special form IIF, import Math and Environment methods + BuiltIns.AddMethod(BuiltInPrintMethod, "print"); + BuiltIns.AddMethod(BuiltInFormatMethod, "format"); + BuiltIns.AddSpecialForm(SpecialFormsLibrary.Iif, "iif", 3, 3); + BuiltIns.ImportStaticMembers(typeof(System.Math)); + BuiltIns.ImportStaticMembers(typeof(Environment)); + } + + //Built-in methods + private object BuiltInPrintMethod(ScriptThread thread, object[] args) { + string text = string.Empty; + switch(args.Length) { + case 1: + text = string.Empty + args[0]; //compact and safe conversion ToString() + break; + case 0: + break; + default: + text = string.Join(" ", args); + break; + } + thread.App.WriteLine(text); + return null; + } + private object BuiltInFormatMethod(ScriptThread thread, object[] args) { + if (args == null || args.Length == 0) return null; + var template = args[0] as string; + if (template == null) + this.ThrowScriptError("Format template must be a string."); + if (args.Length == 1) return template; + //create formatting args array + var formatArgs = args.Skip(1).ToArray(); + var text = string.Format(template, formatArgs); + return text; + + } + } +} diff --git a/Irony.Interpreter/_about_Irony_Interpreter.txt b/Irony.Interpreter/_about_Irony_Interpreter.txt new file mode 100644 index 0000000..889d11f --- /dev/null +++ b/Irony.Interpreter/_about_Irony_Interpreter.txt @@ -0,0 +1,15 @@ + +Some notes on Irony Interpreter implementation + +Goals and non-goals +1. Interpreter should be able to support more-less straightforward implementation of a typical dynamic/scripting languages like Python, Ruby or Lua - except maybe most fancy features like continuations and co-routines. +2. Interperter must be free-threaded - it should allow execution of scripts in parallel on multiple threads. Interpreter should not crash or "loose" data because of concurrent access from different threads. Note that we do NOT intend to solve automatically the concurrency problems of scripting code: script author should use some locking mechanism to preserve integrity of complex data. But no matter what kind of actions script code performs on concurrent threads, the data is not corrupted or lost by the interpreter. +3. And of course, we want a good performance. Good enough for implementations to compete with "native" implementations. +4. Non-goals: this version of interpreter will not support such advanced features as continuations, co-routines, and fibers (light threads). This is a minor setback for languages like Ruby, but a major trouble for Scheme - continuations are important part of the languages. Scheme will have to wait for Interpreter2 - more sophisticated version which I plan to build in the future (and already have some designs and code for it). + + + + + + + diff --git a/Irony.Interpreter/irony.snk b/Irony.Interpreter/irony.snk new file mode 100644 index 0000000000000000000000000000000000000000..54cbccafbe6b236237b6121accaa41c9eda2879e GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098esO)*A(5h&tiAndLr6d3G9>KEc8dCcb zt_nSqESh#h$)?C4mA>DlbX)I1O65dZQ#Uzx)$Ekk8-AppGzA9Gy&}Yv?X(2!dmUeDg;{ZAXVCq^bGgnKhxM=%MYN;LcV}dPdcpZ8(9F{m0a04BG#n; zy(R%=r6lz@1?t^Dte@T?rPhH(g9~F*dvL;oKMvdTjGW%2k}4sTm|(B=`_`2hmp?r} zViveL&d5e0a{oj)TVzA?vK-+$G&4&vDL#UXg0MmvBMF^xh9a^PFuv^ zi>_dWoz%L)9BD2`og(&jQGpLdMbYcY1~$E}A&DWPd_tBM*X4m}NC!&^H)aLxAKy_9 zdfxr-a|@4QN>OL?%{v~Jb^Z97bb^WS^WtJ43h6`0p**Q0igef<2jQnJ1H?P~ + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {5A31B964-CDFA-4406-9316-32A0F04CE149} + Exe + Properties + Irony.Samples.Console + Irony.Samples.Console + v4.0 + 512 + + + + + + + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {AD263C0B-99D3-40A9-9DBF-9086CC524A0B} + 020.Irony.Samples.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + \ No newline at end of file diff --git a/Irony.Samples.Console/025.Irony.Samples.Console.2010.csproj.vspscc b/Irony.Samples.Console/025.Irony.Samples.Console.2010.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/Irony.Samples.Console/025.Irony.Samples.Console.2010.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Irony.Samples.Console/025.Irony.Samples.Console.csproj b/Irony.Samples.Console/025.Irony.Samples.Console.csproj new file mode 100644 index 0000000..e111b14 --- /dev/null +++ b/Irony.Samples.Console/025.Irony.Samples.Console.csproj @@ -0,0 +1,73 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {5A31B964-CDFA-4406-9316-32A0F04CE149} + Exe + Properties + Irony.Samples.Console + Irony.Samples.Console + v3.5 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + {AD263C0B-99D3-40A9-9DBF-9086CC524A0B} + 020.Irony.Samples + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony + + + + + \ No newline at end of file diff --git a/Irony.Samples.Console/Program.cs b/Irony.Samples.Console/Program.cs new file mode 100644 index 0000000..2ac11b3 --- /dev/null +++ b/Irony.Samples.Console/Program.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using Irony.Parsing; +using Irony.Interpreter; +using Irony.Samples; + +namespace Irony.Samples.ConsoleCalculator { + class Program { + + static void Main(string[] args) { + Console.Title = "Irony Console Sample"; + Console.WriteLine("Irony Console Sample."); + Console.WriteLine(""); + Console.WriteLine("Select a grammar to load:"); + Console.WriteLine(" 1. Expression Evaluator"); + Console.WriteLine(" 2. mini-Python"); + Console.WriteLine(" Or press any other key to exit."); + Console.WriteLine(""); + Console.Write("?"); + var choice = Console.ReadLine(); + Grammar grammar; + switch (choice) { + case "1": + grammar = new SampleExpressionEvaluatorGrammar(); + break; + case "2": + grammar = new MiniPython.MiniPythonGrammar(); + break; + default: + return; + } + Console.Clear(); + var language = new LanguageData(grammar); + var runtime = new LanguageRuntime(language); + var commandLine = new CommandLine(runtime); + commandLine.Run(); + } + + }//class +} diff --git a/Irony.Samples.Console/Properties/AssemblyInfo.cs b/Irony.Samples.Console/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7225e2b --- /dev/null +++ b/Irony.Samples.Console/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Irony.Samples.Console")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Irony.Samples.Console")] +[assembly: AssemblyCopyright("Copyright © Roman Ivantsov 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("af5ed259-3327-48e9-a0ad-ac6bcde974ce")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Irony.Samples.Console/app.config b/Irony.Samples.Console/app.config new file mode 100644 index 0000000..f76deb9 --- /dev/null +++ b/Irony.Samples.Console/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Irony.Samples/020.Irony.Samples.2010.csproj b/Irony.Samples/020.Irony.Samples.2010.csproj new file mode 100644 index 0000000..cb34abf --- /dev/null +++ b/Irony.Samples/020.Irony.Samples.2010.csproj @@ -0,0 +1,175 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {AD263C0B-99D3-40A9-9DBF-9086CC524A0B} + Library + Properties + Irony.Samples + Irony.Samples + + + 3.5 + + + v4.0 + + + + + + + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + AllRules.ruleset + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Irony.Samples/020.Irony.Samples.2010.csproj.vspscc b/Irony.Samples/020.Irony.Samples.2010.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/Irony.Samples/020.Irony.Samples.2010.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Irony.Samples/CSharp/CSharpGrammar.cs b/Irony.Samples/CSharp/CSharpGrammar.cs new file mode 100644 index 0000000..c1ec6f0 --- /dev/null +++ b/Irony.Samples/CSharp/CSharpGrammar.cs @@ -0,0 +1,881 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; +using System.Globalization; + +namespace Irony.Samples.CSharp { + + //Full c# 3.0 grammar; all but 2 features are not implemented: + // - preprocessor directives (currently treated as comment lines) + // - LINQ query expressions. + + #region current conflicts explanations + /* + Shift-reduce conflict in state S88, reduce production: using_directives_opt -> on inputs: extern + - because of use of "extern" two places: in "extern alias someName;" and as modifier of class members; + this conflict is not in original c# grammar, it is a result of grammar tweaking (merging modifiers definitions) + prefering shift is correct behavior + Shift-reduce conflict in state S518, reduce production: else_clause_opt -> on inputs: else + - "dangling ELSE conflict" well described in textbooks; preferring shift is a correct behavior + */ + #endregion + + [Language("c#", "3.5", "Sample c# grammar")] + public class CSharpGrammar : Grammar { + TerminalSet _skipTokensInPreview = new TerminalSet(); //used in token preview for conflict resolution + public CSharpGrammar() { + this.GrammarComments = "NOTE: This grammar is just a demo, and it is a broken demo.\r\n" + + "Demonstrates token preview technique to help parser resolve conflicts.\r\n"; + #region Lexical structure + StringLiteral StringLiteral = TerminalFactory.CreateCSharpString("StringLiteral"); + StringLiteral CharLiteral = TerminalFactory.CreateCSharpChar("CharLiteral"); + NumberLiteral Number = TerminalFactory.CreateCSharpNumber("Number"); + IdentifierTerminal identifier = TerminalFactory.CreateCSharpIdentifier("Identifier"); + + CommentTerminal SingleLineComment = new CommentTerminal("SingleLineComment", "//", "\r", "\n", "\u2085", "\u2028", "\u2029"); + CommentTerminal DelimitedComment = new CommentTerminal("DelimitedComment", "/*", "*/"); + NonGrammarTerminals.Add(SingleLineComment); + NonGrammarTerminals.Add(DelimitedComment); + //Temporarily, treat preprocessor instructions like comments + CommentTerminal ppInstruction = new CommentTerminal("ppInstruction", "#", "\n"); + NonGrammarTerminals.Add(ppInstruction); + + //Symbols + KeyTerm colon = ToTerm(":", "colon"); + KeyTerm semi = ToTerm(";", "semi"); + NonTerminal semi_opt = new NonTerminal("semi?"); + semi_opt.Rule = Empty | semi; + KeyTerm dot = ToTerm(".", "dot"); + KeyTerm comma = ToTerm(",", "comma"); + NonTerminal comma_opt = new NonTerminal("comma_opt", Empty | comma); + NonTerminal commas_opt = new NonTerminal("commas_opt"); + commas_opt.Rule = MakeStarRule(commas_opt, null, comma); + KeyTerm qmark = ToTerm("?", "qmark"); + NonTerminal qmark_opt = new NonTerminal("qmark_opt", Empty | qmark); + KeyTerm Lbr = ToTerm("{"); + KeyTerm Rbr = ToTerm("}"); + KeyTerm Lpar = ToTerm("("); + KeyTerm Rpar = ToTerm(")"); + KeyTerm tgoto = ToTerm("goto"); + KeyTerm yld = ToTerm("yield"); + + KeyTerm Lparx = ToTerm("(*"); + #endregion + + #region NonTerminals + //B.2.1. Basic concepts + var qual_name_with_targs = new NonTerminal("qual_name_with_targs"); + var base_type_list = new NonTerminal("base_type_list"); + var generic_dimension_specifier = new NonTerminal("generic_dimension_specifier"); + var qual_name_segment = new NonTerminal("qual_name_segment"); + var qual_name_segments_opt = new NonTerminal("qual_name_segments_opt"); + var type_or_void = new NonTerminal("type_or_void", "type or void"); + var builtin_type = new NonTerminal("builtin_type", "built-in type"); + var type_ref_list = new NonTerminal("type_ref_list"); + var identifier_ext = new NonTerminal("identifier_ext"); + var identifier_or_builtin = new NonTerminal("identifier_or_builtin"); + + //B.2.2. Types + var type_ref = new NonTerminal("type_ref"); + var new_type_ref = new NonTerminal("new_type_ref"); + var type_argument_list = new NonTerminal("type_argument_list"); + var typearg_or_gendimspec_list = new NonTerminal("typearg_or_gendimspec_list"); + var type_argument_list_opt = new NonTerminal("type_argument_list_opt"); + var integral_type = new NonTerminal("integral_type"); + + //B.2.4. Expressions + var argument = new NonTerminal("argument"); + var argument_list = new NonTerminal("argument_list"); + var argument_list_opt = new NonTerminal("argument_list_opt"); + var expression = new NonTerminal("expression", "expression"); + var expression_list = new NonTerminal("expression_list"); + var expression_opt = new NonTerminal("expression_opt"); + var conditional_expression = new NonTerminal("conditional_expression"); + var lambda_expression = new NonTerminal("lambda_expression"); + var query_expression = new NonTerminal("query_expression"); + var unary_operator = new NonTerminal("unary_operator"); + var assignment_operator = new NonTerminal("assignment_operator"); + var primary_expression = new NonTerminal("primary_expression"); + var unary_expression = new NonTerminal("unary_expression"); + var pre_incr_decr_expression = new NonTerminal("pre_incr_decr_expression"); + var post_incr_decr_expression = new NonTerminal("post_incr_decr_expression"); + var primary_no_array_creation_expression = new NonTerminal("primary_no_array_creation_expression"); + var literal = new NonTerminal("literal"); + var parenthesized_expression = new NonTerminal("parenthesized_expression"); + var member_access = new NonTerminal("member_access"); + var member_access_segment = new NonTerminal("member_access_segment"); + var member_access_segments_opt = new NonTerminal("member_access_segments_opt"); + var array_indexer = new NonTerminal("array_indexer"); + var argument_list_par = new NonTerminal("argument_list_par"); + var argument_list_par_opt = new NonTerminal("argument_list_par_opt"); + var incr_or_decr = new NonTerminal("incr_or_decr"); + var incr_or_decr_opt = new NonTerminal("incr_or_decr_opt"); + var creation_args = new NonTerminal("creation_args"); + var object_creation_expression = new NonTerminal("object_creation_expression"); + // delegate creation is syntactically equiv to object creation + //var delegate_creation_expression = new NonTerminal("delegate_creation_expression"); + var anonymous_object_creation_expression = new NonTerminal("anonymous_object_creation_expression"); + var typeof_expression = new NonTerminal("typeof_expression"); + var checked_expression = new NonTerminal("checked_expression"); + var unchecked_expression = new NonTerminal("unchecked_expression"); + var default_value_expression = new NonTerminal("default_value_expression"); + var anonymous_method_expression = new NonTerminal("anonymous_method_expression"); + + var elem_initializer = new NonTerminal("elem_initializer"); + var elem_initializer_list = new NonTerminal("elem_initializer_list"); + var elem_initializer_list_ext = new NonTerminal("elem_initializer_list_ext"); + var initializer_value = new NonTerminal("initializer_value"); + + var anonymous_object_initializer = new NonTerminal("anonymous_object_initializer"); + var member_declarator = new NonTerminal("member_declarator"); + var member_declarator_list = new NonTerminal("member_declarator_list"); + var unbound_type_name = new NonTerminal("unbound_type_name"); + var generic_dimension_specifier_opt = new NonTerminal("generic_dimension_specifier_opt"); + var anonymous_function_signature = new NonTerminal("anonymous_function_signature"); + var anonymous_function_signature_opt = new NonTerminal("anonymous_function_signature_opt"); + var anonymous_function_parameter = new NonTerminal("anonymous_function_parameter"); + var anonymous_function_parameter_decl = new NonTerminal("anonymous_function_parameter_decl"); + var anonymous_function_parameter_list_opt = new NonTerminal("anonymous_function_parameter_list_opt"); + var anonymous_function_parameter_modifier_opt = new NonTerminal("anonymous_function_parameter_modifier_opt"); + var anonymous_function_body = new NonTerminal("anonymous_function_body"); + var lambda_function_signature = new NonTerminal("lambda_function_signature"); + var bin_op_expression = new NonTerminal("bin_op_expression"); + var typecast_expression = new NonTerminal("typecast_expression"); + var bin_op = new NonTerminal("bin_op", "operator symbol"); + + //B.2.5. Statements + var statement = new NonTerminal("statement", "statement"); + var statement_list = new NonTerminal("statement_list"); + var statement_list_opt = new NonTerminal("statement_list_opt"); + var labeled_statement = new NonTerminal("labeled_statement"); + var declaration_statement = new NonTerminal("declaration_statement"); + var embedded_statement = new NonTerminal("embedded_statement"); + var selection_statement = new NonTerminal("selection_statement"); + var iteration_statement = new NonTerminal("iteration_statement"); + var jump_statement = new NonTerminal("jump_statement"); + var try_statement = new NonTerminal("try_statement"); + var checked_statement = new NonTerminal("checked_statement"); + var unchecked_statement = new NonTerminal("unchecked_statement"); + var lock_statement = new NonTerminal("lock_statement"); + var using_statement = new NonTerminal("using_statement"); + var yield_statement = new NonTerminal("yield_statement"); + var block = new NonTerminal("block"); + var statement_expression = new NonTerminal("statement_expression"); + var statement_expression_list = new NonTerminal("statement_expression_list"); + var local_variable_declaration = new NonTerminal("local_variable_declaration"); + var local_constant_declaration = new NonTerminal("local_constant_declaration"); + var local_variable_type = new NonTerminal("local_variable_type"); + var local_variable_declarator = new NonTerminal("local_variable_declarator"); + var local_variable_declarators = new NonTerminal("local_variable_declarators"); + var if_statement = new NonTerminal("if_statement"); + var switch_statement = new NonTerminal("switch_statement"); + var else_clause_opt = new NonTerminal("else_clause_opt"); + var switch_section = new NonTerminal("switch_section"); + var switch_sections_opt = new NonTerminal("switch_sections_opt"); + var switch_label = new NonTerminal("switch_label"); + var switch_labels = new NonTerminal("switch_labels"); + var while_statement = new NonTerminal("while_statement"); + var do_statement = new NonTerminal("do_statement"); + var for_statement = new NonTerminal("for_statement"); + var foreach_statement = new NonTerminal("foreach_statement"); + var for_initializer_opt = new NonTerminal("for_initializer_opt"); + var for_condition_opt = new NonTerminal("for_condition_opt"); + var for_iterator_opt = new NonTerminal("for_iterator_opt"); + var break_statement = new NonTerminal("break_statement"); + var continue_statement = new NonTerminal("continue_statement"); + var goto_statement = new NonTerminal("goto_statement"); + var return_statement = new NonTerminal("return_statement"); + var throw_statement = new NonTerminal("throw_statement"); + var try_clause = new NonTerminal("try_clause"); + var try_clauses = new NonTerminal("try_clauses"); + + var catch_clause = new NonTerminal("catch_clause"); + var finally_clause = new NonTerminal("finally_clause"); + var catch_specifier_opt = new NonTerminal("catch_specifier_opt"); + var identifier_opt = new NonTerminal("identifier_opt"); + + var resource_acquisition = new NonTerminal("resource_acquisition"); + + //namespaces, compilation units + var compilation_unit = new NonTerminal("compilation_unit"); + var extern_alias_directive = new NonTerminal("extern_alias_directive"); + var extern_alias_directives_opt = new NonTerminal("extern_alias_directives_opt"); + var using_directive = new NonTerminal("using_directive"); + var using_directives = new NonTerminal("using_directives"); + var using_directives_opt = new NonTerminal("using_directives_opt"); + var namespace_declaration = new NonTerminal("namespace_declaration"); + var namespace_declarations_opt = new NonTerminal("namespace_declarations_opt"); + var qualified_identifier = new NonTerminal("qualified_identifier"); + var namespace_body = new NonTerminal("namespace_body"); + var namespace_member_declaration = new NonTerminal("namespace_member_declaration"); + var namespace_member_declarations = new NonTerminal("namespace_member_declarations"); + var using_alias_directive = new NonTerminal("using_alias_directive"); + var using_ns_directive = new NonTerminal("using_ns_directive"); + var type_declaration = new NonTerminal("type_declaration"); + var class_declaration = new NonTerminal("class_declaration"); + var delegate_declaration = new NonTerminal("delegate_declaration"); + var qualified_alias_member = new NonTerminal("qualified_alias_member"); + var class_body = new NonTerminal("class_body"); + + //B.2.7 Classes + Terminal partial = ToTerm("partial"); + var type_parameter_list_opt = new NonTerminal("type_parameter_list_opt"); + var type_parameter = new NonTerminal("type_parameter"); + var type_parameters = new NonTerminal("type_parameters"); + var bases_opt = new NonTerminal("bases_opt"); + var type_parameter_constraints_clause = new NonTerminal("type_parameter_constraints_clause"); + var type_parameter_constraints_clauses_opt = new NonTerminal("type_parameter_constraints_clauses"); + var type_parameter_constraint = new NonTerminal("type_parameter_constraint"); + var type_parameter_constraints = new NonTerminal("type_parameter_constraints"); + var member_declaration = new NonTerminal("member_declaration"); + var member_declarations_opt = new NonTerminal("member_declarations_opt"); + var constant_declaration = new NonTerminal("constant_declaration"); + var field_declaration = new NonTerminal("field_declaration"); + var method_declaration = new NonTerminal("method_declaration"); + var property_declaration = new NonTerminal("property_declaration"); + var event_declaration = new NonTerminal("event_declaration"); + var indexer_declaration = new NonTerminal("indexer_declaration"); + var constructor_declaration = new NonTerminal("constructor_declaration"); + var destructor_declaration = new NonTerminal("destructor_declaration"); + var constant_declarator = new NonTerminal("constant_declarator"); + var constant_declarators = new NonTerminal("constant_declarators"); + var modifier = new NonTerminal("modifier"); + var modifiers_opt = new NonTerminal("modifiers_opt"); + var member_header = new NonTerminal("member_header"); + var accessor_name = new NonTerminal("accessor_name"); + var accessor_declaration = new NonTerminal("accessor_declaration"); + var accessor_declarations = new NonTerminal("accessor_declarations"); + var accessor_modifier_opt = new NonTerminal("accessor_modifier_opt"); + var event_body = new NonTerminal("event_body"); + var event_accessor_declarations = new NonTerminal("event_accessor_declarations"); + var add_accessor_declaration = new NonTerminal("add_accessor_declaration"); + var remove_accessor_declaration = new NonTerminal("remove_accessor_declaration"); + var indexer_name = new NonTerminal("indexer_name"); + var operator_declaration = new NonTerminal("operator_declaration"); + var conversion_operator_declaration = new NonTerminal("conversion_operator_declaration"); + var overloadable_operator = new NonTerminal("overloadable_operator"); + var operator_parameter = new NonTerminal("operator_parameter"); + var operator_parameters = new NonTerminal("operator_parameters"); + var conversion_operator_kind = new NonTerminal("conversion_operator_kind"); + var constructor_initializer_opt = new NonTerminal("constructor_initializer_opt"); + var constructor_base = new NonTerminal("constructor_base"); + var variable_declarator = new NonTerminal("variable_declarator"); + var variable_declarators = new NonTerminal("variable_declarators"); + var method_body = new NonTerminal("method_body"); + var formal_parameter_list = new NonTerminal("formal_parameter_list"); + var formal_parameter_list_par = new NonTerminal("formal_parameter_list_par"); + var fixed_parameter = new NonTerminal("fixed_parameter"); + var fixed_parameters = new NonTerminal("fixed_parameters"); + var parameter_modifier_opt = new NonTerminal("parameter_modifier_opt"); + var parameter_array = new NonTerminal("parameter_array"); + + //B.2.8 struct + var struct_declaration = new NonTerminal("struct_declaration"); + var struct_body = new NonTerminal("struct_body"); + + //B.2.9. Arrays + var rank_specifier = new NonTerminal("rank_specifier"); + var rank_specifiers = new NonTerminal("rank_specifiers"); + var rank_specifiers_opt = new NonTerminal("rank_specifiers_opt"); + var dim_specifier = new NonTerminal("dim_specifier"); + var dim_specifier_opt = new NonTerminal("dim_specifier_opt"); + var list_initializer = new NonTerminal("array_initializer"); + var list_initializer_opt = new NonTerminal("array_initializer_opt"); + + + //B.2.10 Interfaces + var interface_declaration = new NonTerminal("interface_declaration"); + var interface_body = new NonTerminal("interface_body"); + var interface_member_declaration = new NonTerminal("interface_member_declaration"); + var interface_member_declarations = new NonTerminal("interface_member_declarations"); + var interface_method_declaration = new NonTerminal("interface_method_declaration"); + var interface_property_declaration = new NonTerminal("interface_property_declaration"); + var interface_event_declaration = new NonTerminal("interface_event_declaration"); + var interface_indexer_declaration = new NonTerminal("interface_indexer_declaration"); + var new_opt = new NonTerminal("new_opt"); + var interface_accessor = new NonTerminal("interface_get_accessor"); + var interface_accessors = new NonTerminal("interface_accessors"); + + //B.2.11 Enums + var enum_declaration = new NonTerminal("enum_declaration"); + var enum_base_opt = new NonTerminal("enum_base_opt"); + var enum_member_declaration = new NonTerminal("enum_member_declaration"); + var enum_member_declarations = new NonTerminal("enum_member_declarations"); + + //B.2.13 Attributes + var attribute_section = new NonTerminal("attribute_section"); + var attributes_opt = new NonTerminal("attributes_opt"); + var attribute_target_specifier_opt = new NonTerminal("attribute_target_specifier_opt"); + var attribute_target = new NonTerminal("attribute_target"); + var attribute = new NonTerminal("attribute"); + var attribute_list = new NonTerminal("attribute_list"); + var attribute_arguments_opt = new NonTerminal("attribute_arguments"); + var named_argument = new NonTerminal("named_argument"); + var attr_arg = new NonTerminal("attr_arg"); + var attribute_arguments_par_opt = new NonTerminal("attribute_arguments_par_opt"); + + + #endregion + + #region operators, punctuation and delimiters + RegisterOperators(1, "||"); + RegisterOperators(2, "&&"); + RegisterOperators(3, "|"); + RegisterOperators(4, "^"); + RegisterOperators(5, "&"); + RegisterOperators(6, "==", "!="); + RegisterOperators(7, "<", ">", "<=", ">=", "is", "as"); + RegisterOperators(8, "<<", ">>"); + RegisterOperators(9, "+", "-"); + RegisterOperators(10, "*", "/", "%"); + //RegisterOperators(11, "."); + // RegisterOperators(12, "++", "--"); + #region comments + //The following makes sense, if you think about "?" in context of operator precedence. + // What we say here is that "?" has the lowest priority among arithm operators. + // Therefore, the parser should prefer reduce over shift when input symbol is "?". + // For ex., when seeing ? in expression "a + b?...", the parser will perform Reduce: + // (a + b)->expr + // and not shift the "?" symbol. + // Same goes for ?? symbol + #endregion + RegisterOperators(-3, "=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>="); + RegisterOperators(-2, "?"); + RegisterOperators(-1, "??"); + + this.Delimiters = "{}[](),:;+-*/%&|^!~<>="; + this.MarkPunctuation(";", ",", "(", ")", "{", "}", "[", "]", ":"); + this.MarkTransient(namespace_member_declaration, member_declaration, type_declaration, statement, embedded_statement, expression, + literal, bin_op, primary_expression, expression); + + this.AddTermsReportGroup("assignment", "=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>="); + this.AddTermsReportGroup("typename", "bool", "decimal", "float", "double", "string", "object", + "sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "char"); + this.AddTermsReportGroup("statement", "if", "switch", "do", "while", "for", "foreach", "continue", "goto", "return", "try", "yield", + "break", "throw", "unchecked", "using"); + this.AddTermsReportGroup("type declaration", "public", "private", "protected", "static", "internal", "sealed", "abstract", "partial", + "class", "struct", "delegate", "interface", "enum"); + this.AddTermsReportGroup("member declaration", "virtual", "override", "readonly", "volatile", "extern"); + this.AddTermsReportGroup("constant", Number, StringLiteral, CharLiteral); + this.AddTermsReportGroup("constant", "true", "false", "null"); + + this.AddTermsReportGroup("unary operator", "+", "-", "!", "~"); + + this.AddToNoReportGroup(comma, semi); + this.AddToNoReportGroup("var", "const", "new", "++", "--", "this", "base", "checked", "lock", "typeof", "default", + "{", "}", "["); + + // + #endregion + + #region "<" conflict resolution + var gen_lt = new NonTerminal("gen_lt"); + gen_lt.Rule = CustomActionHere(this.ResolveLessThanConflict) + "<"; + #endregion + /* + #region Keywords + string strKeywords = + "abstract as base bool break byte case catch char checked " + + "class const continue decimal default delegate do double else enum event explicit extern false finally " + + "fixed float for foreach goto if implicit in int interface internal is lock long namespace " + + "new null object operator out override params private protected public " + + "readonly ref return sbyte sealed short sizeof stackalloc static string " + + "struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual void " + + "volatile while"; + AddKeywordList(strKeywords); + #endregion + */ + + // RULES + //B.2.1. Basic concepts + //qual_name_with_targs is an alias for namespace-name, namespace-or-type-name, type-name, + + generic_dimension_specifier.Rule = gen_lt + commas_opt + ">"; + qual_name_segments_opt.Rule = MakeStarRule(qual_name_segments_opt, null, qual_name_segment); + identifier_or_builtin.Rule = identifier | builtin_type; + identifier_ext.Rule = identifier_or_builtin | "this" | "base"; + qual_name_segment.Rule = dot + identifier + | "::" + identifier + | type_argument_list; + //generic_dimension_specifier.Rule = lt + commas_opt + ">"; + generic_dimension_specifier.Rule = gen_lt + commas_opt + ">"; + qual_name_with_targs.Rule = identifier_or_builtin + qual_name_segments_opt; + + type_argument_list.Rule = gen_lt + type_ref_list + ">"; + type_argument_list_opt.Rule = Empty | type_argument_list; + typearg_or_gendimspec_list.Rule = type_argument_list | generic_dimension_specifier_opt; + + //B.2.2. Types + type_or_void.Rule = qual_name_with_targs | "void"; + builtin_type.Rule = integral_type | "bool" | "decimal" | "float" | "double" | "string" | "object"; + + type_ref.Rule = type_or_void + qmark_opt + rank_specifiers_opt + typearg_or_gendimspec_list; + type_ref_list.Rule = MakePlusRule(type_ref_list, comma, type_ref); + + var comma_list_opt = new NonTerminal("comma_list_opt"); + comma_list_opt.Rule = MakeStarRule(comma_list_opt, comma); + rank_specifier.Rule = "[" + comma_list_opt + "]"; + rank_specifiers.Rule = MakePlusRule(rank_specifiers, null, rank_specifier); + rank_specifiers_opt.Rule = rank_specifiers.Q(); + integral_type.Rule = ToTerm("sbyte") | "byte" | "short" | "ushort" | "int" | "uint" | "long" | "ulong" | "char"; + + //B.2.4. Variables + //Quite strange in specs - + // variable-reference: + // expression + // Is that case it would be possible to do the following: + // GetMyStuff(out (a+b)); + // but MS c# rejects it + + //B.2.4. Expressions + argument.Rule = expression | "ref" + identifier | "out" + identifier; + argument_list.Rule = MakePlusRule(argument_list, comma, argument); + argument_list_opt.Rule = Empty | argument_list; + expression.Rule = conditional_expression + | bin_op_expression + | typecast_expression + | primary_expression; + expression_opt.Rule = Empty | expression; + expression_list.Rule = MakePlusRule(expression_list, comma, expression); + unary_operator.Rule = ToTerm("+") | "-" | "!" | "~" | "*"; + assignment_operator.Rule = ToTerm("=") | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>="; + conditional_expression.Rule = expression + PreferShiftHere() + qmark + expression + colon + expression;// + ReduceThis(); + bin_op_expression.Rule = expression + bin_op + expression; + + typecast_expression.Rule = parenthesized_expression + primary_expression; + primary_expression.Rule = + literal + | unary_expression + | parenthesized_expression + | member_access + | pre_incr_decr_expression + | post_incr_decr_expression + | object_creation_expression + | anonymous_object_creation_expression + | typeof_expression + | checked_expression + | unchecked_expression + | default_value_expression + | anonymous_method_expression; + unary_expression.Rule = unary_operator + primary_expression; + dim_specifier.Rule = "[" + expression_list + "]"; + dim_specifier_opt.Rule = dim_specifier.Q(); + literal.Rule = Number | StringLiteral | CharLiteral | "true" | "false" | "null"; + parenthesized_expression.Rule = Lpar + expression + Rpar; + pre_incr_decr_expression.Rule = incr_or_decr + member_access; + post_incr_decr_expression.Rule = member_access + incr_or_decr; + + //joined invocation_expr and member_access; for member access left the most general variant + member_access.Rule = identifier_ext + member_access_segments_opt; + member_access_segments_opt.Rule = MakeStarRule(member_access_segments_opt, null, member_access_segment); + member_access_segment.Rule = dot + identifier + | array_indexer + | argument_list_par + | type_argument_list; + array_indexer.Rule = "[" + expression_list + "]"; + + argument_list_par.Rule = Lpar + argument_list_opt + Rpar; + + argument_list_par_opt.Rule = Empty | argument_list_par; + + list_initializer.Rule = Lbr + elem_initializer_list_ext + Rbr; + list_initializer_opt.Rule = list_initializer.Q(); + + elem_initializer.Rule = initializer_value | identifier + "=" + initializer_value; + elem_initializer_list.Rule = MakePlusRule(elem_initializer_list, comma, elem_initializer); + elem_initializer_list_ext.Rule = Empty | elem_initializer_list + comma_opt; + initializer_value.Rule = expression | list_initializer; + + //delegate, anon-object, object + object_creation_expression.Rule = "new" + qual_name_with_targs + qmark_opt + creation_args + list_initializer_opt; + creation_args.Rule = dim_specifier | rank_specifier | argument_list_par; + + anonymous_object_creation_expression.Rule = "new" + anonymous_object_initializer; + anonymous_object_initializer.Rule = Lbr + Rbr | Lbr + member_declarator_list + comma_opt + Rbr; + member_declarator.Rule = expression | identifier + "=" + expression; + member_declarator_list.Rule = MakePlusRule(member_declarator_list, comma, member_declarator); + //typeof + typeof_expression.Rule = "typeof" + Lpar + type_ref + Rpar; + generic_dimension_specifier_opt.Rule = Empty | gen_lt + commas_opt + ">"; + //checked, unchecked + checked_expression.Rule = "checked" + parenthesized_expression; + unchecked_expression.Rule = "unchecked" + parenthesized_expression; + //default-value + default_value_expression.Rule = "default" + Lpar + type_ref + Rpar; + //note: we treat ?? as bin-operation, so null-coalesce-expr used in spec as first (condition) component is replaced with expression + // we resolve all this expr hierarchies of binary expressions using precedence + + //anonymous method and lambda - we join explicit and implicit param definitions, making 'type' element optional + // TODO: add after-parse check for this + anonymous_method_expression.Rule = "delegate" + anonymous_function_signature_opt + block; + lambda_expression.Rule = lambda_function_signature + "=>" + anonymous_function_body; + lambda_function_signature.Rule = anonymous_function_signature | identifier; + anonymous_function_signature.Rule = Lpar + anonymous_function_parameter_list_opt + Rpar; + anonymous_function_signature_opt.Rule = anonymous_function_signature.Q(); + anonymous_function_parameter_modifier_opt.Rule = Empty | "ref" | "out"; + anonymous_function_parameter.Rule = anonymous_function_parameter_modifier_opt + anonymous_function_parameter_decl; + anonymous_function_parameter_decl.Rule = identifier | type_ref + identifier; + anonymous_function_parameter_list_opt.Rule = MakeStarRule(anonymous_function_parameter_list_opt, comma, anonymous_function_parameter_decl); + anonymous_function_body.Rule = expression | block; + + //we don't use grammar expressions to specify operator precedence, so we combine all these grammar elements together + // and define just bin_op_expression. Where to put it? + // In spec: non_assignment_expression.Rule = conditional_expression | lambda_expression | query_expression; + //I think it's a mistake; there must be additional entry here for arithm expressions, so we put them here. + // We also have to add "is" and "as" expressions here, as we don't build entire hierarchy of elements for expressing + // precedence (where they appear in original spec); so we put them here + bin_op.Rule = ToTerm("<") + | "||" | "&&" | "|" | "^" | "&" | "==" | "!=" | ">" | "<=" | ">=" | "<<" | ">>" | "+" | "-" | "*" | "/" | "%" + | "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" + | "is" | "as" | "??"; + + //type_check_expression.Rule = expression + "is" + type_ref | expression + "as" + type_ref; + + //Queries + query_expression.Rule = "from"; + + //B.2.5. Statements + statement.Rule = labeled_statement | declaration_statement | embedded_statement; + statement.ErrorRule = SyntaxError + semi; //skip all until semicolon + statement_list.Rule = MakePlusRule(statement_list, null, statement); + statement_list_opt.Rule = Empty | statement_list; + //labeled_statement + labeled_statement.Rule = identifier + colon + embedded_statement; + //declaration_statement + declaration_statement.Rule = local_variable_declaration + semi | local_constant_declaration + semi; + local_variable_declaration.Rule = local_variable_type + local_variable_declarators; //!!! + local_variable_type.Rule = member_access | "var"; // | builtin_type; //to fix the conflict, changing to member-access here + local_variable_declarator.Rule = identifier | identifier + "=" + initializer_value; + local_variable_declarators.Rule = MakePlusRule(local_variable_declarators, comma, local_variable_declarator); + local_constant_declaration.Rule = "const" + type_ref + constant_declarators; + //embedded_statement + embedded_statement.Rule = block | semi /*empty_statement*/ | statement_expression + semi | selection_statement + | iteration_statement | jump_statement | try_statement | checked_statement | unchecked_statement + | lock_statement | using_statement | yield_statement; + block.Rule = Lbr + statement_list_opt + Rbr; + //selection (if and switch) + selection_statement.Rule = if_statement | switch_statement; + if_statement.Rule = ToTerm("if") + Lpar + expression + Rpar + embedded_statement + else_clause_opt; + else_clause_opt.Rule = Empty | PreferShiftHere() + "else" + embedded_statement; + switch_statement.Rule = "switch" + parenthesized_expression + Lbr + switch_sections_opt + Rbr; + switch_section.Rule = switch_labels + statement_list; + switch_sections_opt.Rule = MakeStarRule(switch_sections_opt, null, switch_section); + switch_label.Rule = "case" + expression + colon | "default" + colon; + switch_labels.Rule = MakePlusRule(switch_labels, null, switch_label); + //iteration statements + iteration_statement.Rule = while_statement | do_statement | for_statement | foreach_statement; + while_statement.Rule = "while" + parenthesized_expression + embedded_statement; + do_statement.Rule = "do" + embedded_statement + "while" + parenthesized_expression + semi; + for_statement.Rule = "for" + Lpar + for_initializer_opt + semi + for_condition_opt + semi + for_iterator_opt + Rpar + embedded_statement; + for_initializer_opt.Rule = Empty | local_variable_declaration | statement_expression_list; + for_condition_opt.Rule = Empty | expression; + for_iterator_opt.Rule = Empty | statement_expression_list; + foreach_statement.Rule = "foreach" + Lpar + local_variable_type + identifier + "in" + expression + Rpar + embedded_statement; + //jump-statement + jump_statement.Rule = break_statement | continue_statement | goto_statement | return_statement | throw_statement; + break_statement.Rule = "break" + semi; + continue_statement.Rule = "continue" + semi; + goto_statement.Rule = tgoto + identifier + semi | tgoto + "case" + expression + semi | tgoto + "default" + semi; + return_statement.Rule = "return" + expression_opt + semi; + throw_statement.Rule = "throw" + expression_opt + semi; + //try-statement + //changed to avoid conflicts; need to check correct ordering of catch/finally clause in after-parse validation + try_statement.Rule = "try" + block + try_clauses; + try_clause.Rule = catch_clause | finally_clause; + try_clauses.Rule = MakePlusRule(try_clauses, null, try_clause); + catch_clause.Rule = "catch" + catch_specifier_opt + block; + finally_clause.Rule = "finally" + block; + catch_specifier_opt.Rule = Empty | Lpar + qual_name_with_targs + identifier_opt + Rpar; + identifier_opt.Rule = Empty | identifier; + //checked, unchecked, locked, using + checked_statement.Rule = "checked" + block; + unchecked_statement.Rule = "unchecked" + block; + lock_statement.Rule = "lock" + parenthesized_expression + embedded_statement; + using_statement.Rule = "using" + Lpar + resource_acquisition + Rpar + embedded_statement; + resource_acquisition.Rule = local_variable_declaration | expression; + //yield statement + yield_statement.Rule = yld + "return" + expression + semi | yld + "break" + semi; + + //expression statement + // expression_statement.Rule = statement_expression + semi; + statement_expression.Rule = object_creation_expression + | member_access | member_access + assignment_operator + expression + | pre_incr_decr_expression | post_incr_decr_expression + ; + statement_expression_list.Rule = MakePlusRule(statement_expression_list, comma, statement_expression); + incr_or_decr_opt.Rule = Empty | ToTerm("++") | "--"; + incr_or_decr.Rule = ToTerm("++") | "--"; + + //B.2.6. Namespaces + this.Root = compilation_unit; + compilation_unit.Rule = extern_alias_directives_opt + + using_directives_opt + + attributes_opt + namespace_declarations_opt; + extern_alias_directive.Rule = PreferShiftHere() + ToTerm("extern") + "alias" + identifier + semi; + extern_alias_directives_opt.Rule = MakeStarRule(extern_alias_directives_opt, null, extern_alias_directive); + namespace_declaration.Rule = "namespace" + qualified_identifier + namespace_body + semi_opt; + namespace_declarations_opt.Rule = MakeStarRule(namespace_declarations_opt, null, namespace_declaration); + qualified_identifier.Rule = MakePlusRule(qualified_identifier, dot, identifier); + + namespace_body.Rule = "{" + extern_alias_directives_opt + using_directives_opt + namespace_member_declarations + "}"; + + using_directive.Rule = using_alias_directive | using_ns_directive; + using_directives.Rule = MakePlusRule(using_directives, null, using_directive); + using_directives_opt.Rule = Empty | using_directives; + + using_alias_directive.Rule = "using" + identifier + "=" + qual_name_with_targs + semi; + using_ns_directive.Rule = "using" + qual_name_with_targs + semi; + namespace_member_declaration.Rule = namespace_declaration | type_declaration; + namespace_member_declarations.Rule = MakePlusRule(namespace_member_declarations, null, namespace_member_declaration); + + type_declaration.Rule = class_declaration | struct_declaration | interface_declaration | enum_declaration | delegate_declaration; + + //B.2.7. Classes + class_declaration.Rule = member_header + "class" + identifier + type_parameter_list_opt + + bases_opt + type_parameter_constraints_clauses_opt + class_body; + class_body.Rule = Lbr + member_declarations_opt + Rbr; + bases_opt.Rule = Empty | colon + base_type_list; + base_type_list.Rule = MakePlusRule(base_type_list, comma, qual_name_with_targs); + + //Type parameters + type_parameter.Rule = attributes_opt + identifier; + type_parameters.Rule = MakePlusRule(type_parameters, comma, type_parameter); + type_parameter_list_opt.Rule = Empty | gen_lt + type_parameters + ">"; + type_parameter_constraints_clause.Rule = "where" + type_parameter + colon + type_parameter_constraints; + type_parameter_constraints.Rule = MakePlusRule(type_parameter_constraints, comma, type_parameter_constraint); + type_parameter_constraints_clauses_opt.Rule = MakeStarRule(type_parameter_constraints_clauses_opt, null, type_parameter_constraints_clause); + //Note for post-processing - make sure the order is correct: new() is always last, etc. See p.503 of the spec + type_parameter_constraint.Rule = qual_name_with_targs | "class" | "struct" | ToTerm("new") + Lpar + Rpar; + + //Class members + //Note: we split operator-declaration into two separate operator elements: bin/unary and conversion operators + // to avoid possible ambiguities and conflicts + member_declaration.Rule = constant_declaration | field_declaration | method_declaration + | property_declaration | event_declaration | indexer_declaration + | operator_declaration | conversion_operator_declaration + | constructor_declaration | destructor_declaration | type_declaration; + member_declaration.ErrorRule = SyntaxError + ";" | SyntaxError + "}" ; + member_declarations_opt.Rule = MakeStarRule(member_declarations_opt, null, member_declaration); + + //Modifiers - see note #1 in Notes.txt file + modifier.Rule = ToTerm("new") | "public" | "protected" | "internal" | "private" | "static" | "virtual" | "sealed" | + "override" | "abstract" | "readonly" | "volatile" | "partial" | "extern"; //!!! + modifiers_opt.Rule = MakeStarRule(modifiers_opt, null, modifier); + //Joined member header - see note #2 + member_header.Rule = attributes_opt + modifiers_opt; + constant_declaration.Rule = member_header + "const" + type_ref + constant_declarators + semi; + constant_declarator.Rule = identifier + "=" + expression; + constant_declarators.Rule = MakePlusRule(constant_declarators, comma, constant_declarator); + field_declaration.Rule = member_header + type_ref + variable_declarators + semi; + variable_declarator.Rule = identifier | identifier + "=" + elem_initializer; + variable_declarators.Rule = MakePlusRule(variable_declarators, comma, variable_declarator); + //See note #3 about merging type_parameter_list into type_arguments of the preceding qual_name. + method_declaration.Rule = member_header + type_ref + qual_name_with_targs // + type_parameter_list.Q() + + formal_parameter_list_par + type_parameter_constraints_clauses_opt + method_body; + formal_parameter_list.Rule = fixed_parameters | fixed_parameters + comma + parameter_array | parameter_array; + formal_parameter_list_par.Rule = Lpar + Rpar | Lpar + formal_parameter_list + Rpar; + + fixed_parameter.Rule = attributes_opt + parameter_modifier_opt + type_ref + identifier; + fixed_parameters.Rule = MakePlusRule(fixed_parameters, comma, fixed_parameter); + parameter_modifier_opt.Rule = Empty | "ref" | "out" | "this"; + parameter_array.Rule = attributes_opt + "params" + type_ref + /*"[" + "]" + */ identifier; + method_body.Rule = block | semi; + // See note #4 about member-name + //TODO: add after-parse validation that no more than one accessor of each type is there. + property_declaration.Rule = member_header + type_ref + qual_name_with_targs/*member-name*/ + Lbr + accessor_declarations + Rbr; + accessor_declaration.Rule = attributes_opt + accessor_modifier_opt + accessor_name + block; + accessor_declarations.Rule = MakePlusRule(accessor_declarations, null, accessor_declaration); + accessor_name.Rule = ToTerm("get") | "set"; + accessor_modifier_opt.Rule = Empty | "protected" | "internal" | "private" | + ToTerm("protected") + "internal" | ToTerm("internal") + "protected"; + + event_declaration.Rule = member_header + "event" + type_ref + event_body; + event_body.Rule = variable_declarators + semi | qual_name_with_targs + Lbr + event_accessor_declarations + Rbr; + event_accessor_declarations.Rule = add_accessor_declaration + remove_accessor_declaration | + remove_accessor_declaration + add_accessor_declaration; + add_accessor_declaration.Rule = attributes_opt + "add" + block; + remove_accessor_declaration.Rule = attributes_opt + "remove" + block; + + //indexer + indexer_declaration.Rule = member_header + type_ref + indexer_name + "[" + formal_parameter_list + "]" + + Lbr + accessor_declarations + Rbr; + indexer_name.Rule = "this" | qual_name_with_targs + dot + "this"; + + //operator + // note: difference with specs - we separate unary/binary operators from conversion operator, + // and join binary and unary operator definitions, see note #5 + operator_declaration.Rule = member_header + type_ref + "operator" + overloadable_operator + Lpar + operator_parameters + Rpar + block; + overloadable_operator.Rule = ToTerm("+") | "-" | "!" | "~" | "++" | "--" | "true" | "false" //unary operators + | "*" | "/" | "%" | "&" | "|" | "^" | "<<" | ">>" | "==" | "!=" | ">" | "<" | ">=" | "<="; + operator_parameters.Rule = operator_parameter | operator_parameter + comma + operator_parameter; + operator_parameter.Rule = type_ref + identifier; + conversion_operator_declaration.Rule = member_header + conversion_operator_kind + + "operator" + type_ref + Lpar + operator_parameter + Rpar + block; + conversion_operator_kind.Rule = ToTerm("implicit") | "explicit"; + + //constructor - also covers static constructor; the only difference is the word static + constructor_declaration.Rule = member_header + identifier + formal_parameter_list_par + + constructor_initializer_opt + block; + constructor_initializer_opt.Rule = Empty | colon + constructor_base + argument_list_par; + constructor_base.Rule = ToTerm("this") | "base"; + + destructor_declaration.Rule = member_header + // changed from Symbol("extern").Q() + "~" + identifier + Lpar + Rpar + block; + + //B.2.8 + struct_declaration.Rule = member_header + "struct" + identifier + type_parameter_list_opt + bases_opt + + type_parameter_constraints_clauses_opt + struct_body; + struct_body.Rule = Lbr + member_declarations_opt + Rbr; + + + //B.2.9. Arrays + + //B.2.10 Interface + interface_declaration.Rule = member_header + "interface" + identifier + type_parameter_list_opt + bases_opt + + type_parameter_constraints_clauses_opt + interface_body; + interface_body.Rule = Lbr + interface_member_declarations + Rbr; + interface_member_declaration.Rule = interface_method_declaration | interface_property_declaration + | interface_event_declaration | interface_indexer_declaration; + interface_member_declarations.Rule = MakePlusRule(interface_member_declarations, null, interface_member_declaration); + interface_method_declaration.Rule = attributes_opt + new_opt + type_ref + identifier + type_parameter_list_opt + + formal_parameter_list_par + type_parameter_constraints_clauses_opt + semi; + //NOte: changing type to type_ref to fix the conflict + //Note: add after-parse validation that no more than one accessor of each type is there. + interface_property_declaration.Rule = attributes_opt + new_opt + type_ref + identifier + Lbr + interface_accessors + Rbr; + interface_accessor.Rule = attributes_opt + accessor_name + semi; + interface_accessors.Rule = MakePlusRule(interface_accessors, null, interface_accessor); + interface_event_declaration.Rule = attributes_opt + new_opt + "event" + type_ref + identifier; + interface_indexer_declaration.Rule = attributes_opt + new_opt + type_ref + "this" + + "[" + formal_parameter_list + "]" + Lbr + interface_accessors + Rbr; + new_opt.Rule = Empty | "new"; + + //B.2.11 Enums + enum_declaration.Rule = member_header + "enum" + identifier + enum_base_opt + Lbr + enum_member_declarations + Rbr + semi_opt; + enum_base_opt.Rule = Empty | colon + integral_type; + enum_member_declaration.Rule = attributes_opt + identifier | attributes_opt + identifier + "=" + expression; + enum_member_declarations.Rule = MakeListRule(enum_member_declarations, comma, enum_member_declaration, TermListOptions.PlusList | TermListOptions.AllowTrailingDelimiter); + + //B.2.12 Delegates + delegate_declaration.Rule = member_header + "delegate" + type_ref + identifier + + type_parameter_list_opt + formal_parameter_list_par + type_parameter_constraints_clauses_opt + semi; + + + //B.2.13. Attributes + + attributes_opt.Rule = MakeStarRule(attributes_opt, null, attribute_section); + attribute_section.Rule = "[" + attribute_target_specifier_opt + attribute_list + comma_opt + "]"; + attribute_list.Rule = MakePlusRule(attribute_list, comma, attribute); + attribute.Rule = qual_name_with_targs + attribute_arguments_par_opt; + + attribute_target_specifier_opt.Rule = Empty | attribute_target + colon; + attribute_target.Rule = ToTerm("field") | "event" | "method" | "param" | "property" | "return" | "type"; + attribute_arguments_par_opt.Rule = Empty | Lpar + attribute_arguments_opt + Rpar; + attribute_arguments_opt.Rule = MakeStarRule(attribute_arguments_opt, comma, attr_arg); + attr_arg.Rule = identifier + "=" + expression | expression; + + //Prepare term set for conflict resolution + _skipTokensInPreview.UnionWith(new Terminal[] { dot, identifier, comma, ToTerm("::"), comma, ToTerm("["), ToTerm("]") }); + + } + + #region conflict resolution for "<" +/* The shift-reduce conflict for "<" symbol is the problem of deciding what is "<" symbol in the input - is it + * opening brace for generic reference, or logical operator. The following is a printout of parser state that has a conflict + * The handling code needs to run ahead and decide a proper action: if we see ">", then it is a generic bracket and we do shift; + * otherwise, it is an operator and we make Reduce + * +State S188 (Inadequate) + Shift items: + member_access_segments_opt -> member_access_segments_opt ·member_access_segment + member_access_segment -> ·. Identifier + member_access_segment -> ·array_indexer + array_indexer -> ·[ expression_list ] + member_access_segment -> ·argument_list_par + argument_list_par -> ·( argument_list_opt ) + member_access_segment -> ·type_argument_list + type_argument_list -> ·_<_ type_ref_list > + _<_ -> ·< + Reduce items: + member_access -> identifier_ext member_access_segments_opt · [? , ) : ; } ] Identifier ++ -- || && | ^ & == != > <= >= << >> + - * / % = += -= *= /= %= &= |= ^= <<= >>= is as ?? <] + Shifts: member_access_segment->S220, .->S221, array_indexer->S222, [->S223, argument_list_par->S224, (->S225, type_argument_list->S226, _<_->S59, + +*/ + //Here is an elaborate generic declaration which can be used as a good test. Perfectly legal, uncomment it to check that c# + // accepts it: + // List> genericVar; + private void ResolveLessThanConflict(ParsingContext context, CustomParserAction customAction) { + var scanner = context.Parser.Scanner; + string previewSym = null; + if (context.CurrentParserInput.Term.Name == "<") { + scanner.BeginPreview(); + int ltCount = 0; + while(true) { + //Find first token ahead (using preview mode) that is either end of generic parameter (">") or something else + Token preview; + do { + preview = scanner.GetToken(); + } while (_skipTokensInPreview.Contains(preview.Terminal) && preview.Terminal != base.Eof); + //See what did we find + previewSym = preview.Terminal.Name; + if (previewSym == "<") + ltCount++; + else if (previewSym == ">" && ltCount > 0) { + ltCount--; + continue; + } else + break; + } + scanner.EndPreview(true); //keep previewed tokens; important to keep ">>" matched to two ">" symbols, not one combined symbol (see method below) + }//if + //if we see ">", then it is type argument, not operator + ParserAction action; + if (previewSym == ">") + action = customAction.ShiftActions.First(a => a.Term.Name == "<"); + else + action = customAction.ReduceActions.First(); + // Actually execute action + action.Execute(context); + } + + //In preview, we may run into combination '>>' which is a comletion of nested generic parameters. + // It should be recognized as two ">" symbols, not a single ">>" operator + // By default, the ">>" has higher priority over single ">" symbol because it is longer. + // When this method is called we force the selection to a single ">" + public override void OnScannerSelectTerminal(ParsingContext context) { + if (context.Source.PreviewChar == '>' && context.Status == ParserStatus.Previewing) { + context.CurrentTerminals.Clear(); + context.CurrentTerminals.Add(ToTerm(">")); //select the ">" terminal + } + base.OnScannerSelectTerminal(context); + } + #endregion + + // See http://www.jaggersoft.com/csharp_standard/9.3.3.htm + public override void SkipWhitespace(ISourceStream source) { + while (!source.EOF()) { + var ch = source.PreviewChar; + switch (ch) { + case ' ': + case '\t': + case '\n': + case '\v': + case '\u2085': + case '\u2028': + case '\u2029': + source.PreviewPosition++; + break; + default: + //Check unicode class Zs + UnicodeCategory chCat = char.GetUnicodeCategory(ch); + if (chCat == UnicodeCategory.SpaceSeparator) //it is whitespace, continue moving + continue;//while loop + //Otherwize return + return; + }//switch + }//while + } + }//class +}//namespace + diff --git a/Irony.Samples/CSharp/Notes.txt b/Irony.Samples/CSharp/Notes.txt new file mode 100644 index 0000000..969d9f2 --- /dev/null +++ b/Irony.Samples/CSharp/Notes.txt @@ -0,0 +1,46 @@ + +1. We had to join all modifiers (for class, field, method, etc) into single "modifier", to avoid inevitable conflicts + modifier.Rule = Symbol("new") | "public" | "protected" | "internal" | "private" | "static" | "virtual" | "sealed" | + "override" | "abstract" | "extern" | "readonly" | "volatile"; + Specific modifiers: + class-modifiers: + new public protected internal private abstract sealed static + field_modifier.Rule = Symbol("new") | "public" | "protected" | "internal" | "private" | "static" | "readonly" | "volatile"; + method_modifier.Rule = Symbol("new") | "public" | "protected" | "internal" | "private" | "static" | "virtual" | "sealed" | + "override" | "abstract" | "extern"; + property modifiers: + new public protected internal private static virtual sealed override abstract extern + indexer-modifiers: + new public protected internal private virtual sealed override abstract extern + constructor-modifiers: + public protected internal private extern + destructor-modifiers: + extern + interface, constant, enum, delegate, struct-modifiers: + new public protected internal private + + TODO: implement after-parse check of each modifier subtype + + +2. Member headers. We merge all header types in one member_header. + TODO: after-parse validation: "partial" specifier and (return_type == void) are allowed only for methods + for static constructor calls to parent constructor are not allowed (like ":base(...)") + +4. Member-name. In spec: + member-name: + identifier + interface-type . identifier + The interface-type element is then defined as general type reference, which can end with plain identifier => we have conflict. + What spec says is that member-name should always end with identifier (not type arg). + We replace member-name with general "qual_name_with_targs" to resolve the conflict, and leave it to after-parse check + to verify the last segment of the name. + TODO: Add after parse check that property's name ends with pure identifier. + +5. Unary/binary operators definitions joined, to avoid conflict (because some symbols like + are both types) + unary-operators: + - ! ~ ++ -- true false + binary-operators: + - * / % & | ^ << right-shift == != > < >= <= + TODO: After parse check of number of arguments + +6. try statement + TODO: add after-parse validation of order of catch/finally clauses + \ No newline at end of file diff --git a/Irony.Samples/DataGrammars/JsonGrammar.cs b/Irony.Samples/DataGrammars/JsonGrammar.cs new file mode 100644 index 0000000..0e39ae5 --- /dev/null +++ b/Irony.Samples/DataGrammars/JsonGrammar.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples.Json { + [Language("JSON", "1.0", "JSON data format")] + public class JsonGrammar : Grammar { + public JsonGrammar() { + //Terminals + var jstring = new StringLiteral("string", "\""); + var jnumber = new NumberLiteral("number"); + var comma = ToTerm(","); + + //Nonterminals + var jobject = new NonTerminal("Object"); + var jobjectBr = new NonTerminal("ObjectBr"); + var jarray = new NonTerminal("Array"); + var jarrayBr = new NonTerminal("ArrayBr"); + var jvalue = new NonTerminal("Value"); + var jprop = new NonTerminal("Property"); + + //Rules + jvalue.Rule = jstring | jnumber | jobjectBr | jarrayBr | "true" | "false" | "null"; + jobjectBr.Rule = "{" + jobject + "}"; + jobject.Rule = MakeStarRule(jobject, comma, jprop); + jprop.Rule = jstring + ":" + jvalue; + jarrayBr.Rule = "[" + jarray + "]"; + jarray.Rule = MakeStarRule(jarray, comma, jvalue); + + //Set grammar root + this.Root = jvalue; + MarkPunctuation("{", "}", "[", "]", ":", ","); + this.MarkTransient(jvalue, jarrayBr, jobjectBr); + + }//constructor + }//class +}//namespace diff --git a/Irony.Samples/DataGrammars/SampleCsvGrammar.cs b/Irony.Samples/DataGrammars/SampleCsvGrammar.cs new file mode 100644 index 0000000..eaddd3b --- /dev/null +++ b/Irony.Samples/DataGrammars/SampleCsvGrammar.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples { + // A sample grammar for reading comma-separated file containing cars information + // Demonstrates use of DsvLiteral. Use sample data file DataFiles\CarModels.csv. + // The data is copied from Wikipedia article about CSV format: http://en.wikipedia.org/wiki/Comma-separated_values + [Language("CarListing-CSV", "1.0", "Grammar for reading CSV files.")] + public class SampleCsvGrammar : Grammar { + public SampleCsvGrammar() { + this.GrammarComments = +@"A sample grammar for reading comma-separated file containing cars information +Demonstrates use of DsvLiteral. Use sample data file DataFiles\CarModels.csv."; + //Terminals + var year = new DsvLiteral("Year", TypeCode.Int16); // comma is default separator + var maker = new DsvLiteral("Maker", TypeCode.String); + var model = new DsvLiteral("Model", TypeCode.String); + var comment = new DsvLiteral("Comment", TypeCode.String); + var price = new DsvLiteral("Price", TypeCode.Double, null); //null means no terminator, look for NewLine + + var line = new NonTerminal("Line"); + var lines = new NonTerminal("Lines"); + var data = new NonTerminal("data"); + + //Rules + line.Rule = year + maker + model + comment + price + NewLine; //we don't specify comma between fields, because our terminals consume separator automatically + lines.Rule = MakeStarRule(lines, line); + data.Rule = lines + NewLineStar; //to allow empty lines after + this.Root = data; + this.LanguageFlags |= LanguageFlags.NewLineBeforeEOF; + + }//constructor + }//class +}//namespace diff --git a/Irony.Samples/FullTextSearchQueryConverter/SearchGrammar.cs b/Irony.Samples/FullTextSearchQueryConverter/SearchGrammar.cs new file mode 100644 index 0000000..3959dc2 --- /dev/null +++ b/Irony.Samples/FullTextSearchQueryConverter/SearchGrammar.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; +using System.Globalization; +//using System.Data.SqlClient; +//using System.Data; + +namespace Irony.Samples.FullTextSearch +{ + [Language("SearchGrammar", "1.0", "Google-to-SQL query converter")] + public class SearchGrammar : Grammar, ICanRunSample { + public SearchGrammar() : base(false) { + this.GrammarComments = + "Google-to-SQL full-text query format converter. Based on original project by Michael Coles.\r\n" + + "http://www.sqlservercentral.com/articles/Full-Text+Search+(2008)/64248/ \r\n" + + "Slightly revised to work with latest version of Irony. "; + + // Terminals + var Term = CreateTerm("Term"); + var Phrase = new StringLiteral("Phrase", "\""); + var ImpliedAnd = new ImpliedSymbolTerminal("ImpliedAnd"); + + // NonTerminals + var BinaryExpression = new NonTerminal("BinaryExpression"); + var BinaryOp = new NonTerminal("BinaryOp"); + var Expression = new NonTerminal("Expression"); + var PrimaryExpression = new NonTerminal("PrimaryExpression"); + var ThesaurusExpression = new NonTerminal("ThesaurusExpression"); + var ThesaurusOperator = new NonTerminal("ThesaurusOperator"); + var ExactExpression = new NonTerminal("ExactExpression"); + var ParenthesizedExpression = new NonTerminal("ParenthesizedExpression"); + var ProximityExpression = new NonTerminal("ProximityExpression"); + var ProximityList = new NonTerminal("ProximityList"); + + this.Root = Expression; + Expression.Rule = PrimaryExpression | BinaryExpression; + BinaryExpression.Rule = Expression + BinaryOp + Expression; + BinaryOp.Rule = ImpliedAnd | "and" | "&" | "-" | "or" | "|"; + PrimaryExpression.Rule = Term + | ThesaurusExpression + | ExactExpression + | ParenthesizedExpression + | Phrase + | ProximityExpression; + ThesaurusExpression.Rule = "~" + Term; + ExactExpression.Rule = "+" + Term | "+" + Phrase; + ParenthesizedExpression.Rule = "(" + Expression + ")"; + ProximityExpression.Rule = "<" + ProximityList + ">"; + MakePlusRule(ProximityList, Term); + + MarkTransient(PrimaryExpression, Expression, ProximityExpression, ParenthesizedExpression, BinaryOp); + MarkPunctuation("<", ">", "(", ")"); + RegisterOperators(10, "or", "|"); + RegisterOperators(20, "and", "&", "-"); + RegisterOperators(20, ImpliedAnd); + //Register brace pairs to improve error reporting + RegisterBracePair("(", ")"); + RegisterBracePair("<", ">"); + //Do not report ImpliedAnd as expected symbol - it is not really a symbol + this.AddToNoReportGroup(ImpliedAnd); + //also do not report braces as expected + this.AddToNoReportGroup("(", ")", "<", ">"); + + } + + //Creates extended identifier terminal that allows international characters + // Following the pattern used for c# identifier terminal in TerminalFactory.CreateCSharpIdentifier method; + private IdentifierTerminal CreateTerm(string name) { + IdentifierTerminal term = new IdentifierTerminal(name, "!@#$%^*_'.?-", "!@#$%^*_'.?0123456789"); + term.CharCategories.AddRange(new UnicodeCategory[] { + UnicodeCategory.UppercaseLetter, //Ul + UnicodeCategory.LowercaseLetter, //Ll + UnicodeCategory.TitlecaseLetter, //Lt + UnicodeCategory.ModifierLetter, //Lm + UnicodeCategory.OtherLetter, //Lo + UnicodeCategory.LetterNumber, //Nl + UnicodeCategory.DecimalDigitNumber, //Nd + UnicodeCategory.ConnectorPunctuation, //Pc + UnicodeCategory.SpacingCombiningMark, //Mc + UnicodeCategory.NonSpacingMark, //Mn + UnicodeCategory.Format //Cf + }); + //StartCharCategories are the same + term.StartCharCategories.AddRange(term.CharCategories); + return term; + } + + public string RunSample(RunSampleArgs args) { + var sql = ConvertQuery(args.ParsedSample.Root); + return sql; + } + + + public enum TermType + { + Inflectional = 1, + Thesaurus = 2, + Exact = 3 + } + public static string ConvertQuery(ParseTreeNode node) { + return ConvertQuery(node, TermType.Inflectional); + } + + private static string ConvertQuery(ParseTreeNode node, TermType type) { + string result = ""; + // Note that some NonTerminals don't actually get into the AST tree, + // because of some Irony's optimizations - punctuation stripping and + // transient nodes elimination. For example, ParenthesizedExpression - parentheses + // symbols get stripped off as punctuation, and child expression node + // (parenthesized content) replaces the parent ParenthesizedExpression node + switch (node.Term.Name) { + case "BinaryExpression": + string opSym = string.Empty; + string op = node.ChildNodes[1].FindTokenAndGetText().ToLower(); + string sqlOp = ""; + switch(op) { + case "": case "&": case "and": + sqlOp = " AND "; + type = TermType.Inflectional; + break; + case "-": + sqlOp = " AND NOT "; + break; + case "|": case "or": + sqlOp = " OR "; + break; + }//switch + + result = "(" + ConvertQuery(node.ChildNodes[0], type) + sqlOp + ConvertQuery(node.ChildNodes[2], type) + ")"; + break; + + case "PrimaryExpression": + result = "(" + ConvertQuery(node.ChildNodes[0], type) + ")"; + break; + + case "ProximityList": + string[] tmp = new string[node.ChildNodes.Count]; + type = TermType.Exact; + for (int i = 0; i < node.ChildNodes.Count; i++) { + tmp[i] = ConvertQuery(node.ChildNodes[i], type); + } + result = "(" + string.Join(" NEAR ", tmp) + ")"; + type = TermType.Inflectional; + break; + + case "Phrase": + result = '"' + node.Token.ValueString + '"'; + break; + + case "ThesaurusExpression": + result = " FORMSOF (THESAURUS, " + + node.ChildNodes[1].Token.ValueString + ") "; + break; + + case "ExactExpression": + result = " \"" + node.ChildNodes[1].Token.ValueString + "\" "; + break; + + case "Term": + switch (type) { + case TermType.Inflectional: + result = node.Token.ValueString; + if (result.EndsWith("*")) + result = "\"" + result + "\""; + else + result = " FORMSOF (INFLECTIONAL, " + result + ") "; + break; + case TermType.Exact: + result = node.Token.ValueString; + + break; + } + break; + + // This should never happen, even if input string is garbage + default: + throw new ApplicationException("Converter failed: unexpected term: " + + node.Term.Name + ". Please investigate."); + + } + return result; + } + /* + private static string connectionString = "DATA SOURCE=(local);INITIAL CATALOG=AdventureWorks;INTEGRATED SECURITY=SSPI;"; + + //commented out, to avoid referencing extra Data/SQL namespaces + public static DataTable ExecuteQuery(string ftsQuery) { + SqlDataAdapter da = null; + DataTable dt = null; + try { + dt = new DataTable(); + da = new SqlDataAdapter + ( + "SELECT ROW_NUMBER() OVER (ORDER BY DocumentId) AS Number, " + + " Title, " + + " DocumentSummary " + + "FROM Production.Document " + + "WHERE CONTAINS(*, @ftsQuery);", + connectionString + ); + da.SelectCommand.Parameters.Add("@ftsQuery", SqlDbType.NVarChar, 4000).Value = ftsQuery; + da.Fill(dt); + da.Dispose(); + } catch (Exception ex) { + if (da != null) + da.Dispose(); + if (dt != null) + dt.Dispose(); + throw (ex); + } + return dt; + } + + */ + } + +} diff --git a/Irony.Samples/GWBasic/GwBasicGrammar.cs b/Irony.Samples/GWBasic/GwBasicGrammar.cs new file mode 100644 index 0000000..032ac6e --- /dev/null +++ b/Irony.Samples/GWBasic/GwBasicGrammar.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples { + /// + /// This class defines the Grammar for the GwBASIC language. + /// (Not complete.) + /// + /// + /// http://www.xs4all.nl/~hwiegman/gw-man/index.html + /// or + /// http://www.geocities.com/KindlyRat/GWBASIC_Help.zip + /// + [Language("GwBasic", "1.01", "Sample GW Basic grammar")] + public class GWBasicGrammar : Grammar { + + public GWBasicGrammar() : base(false) // BASIC is not case sensitive... + { + this.GrammarComments = "This grammar uses one new Irony feature - Scanner-Parser link. Parser helps Scanner to disambiguate " + + "similar but different token types when more than one terminal matches the current input char.\r\n" + + "See comments in GwBasicGrammar.cs file."; + /* + Scanner-Parser link. + The grammar defines 3 terminals for numbers: number, lineNumber and fileNumber. All three return decimal + digits in GetFirsts() method. So when current input is a digit, the scanner has 3 potential candidate terminals + to match the input. However, because each of the 3 terminals can appear in specific "places" in grammar, + the parser is able to assist scanner to pick the correct terminal, depending on the current parser state. + The disambiguation happens in Scanner.SelectTerminals method. When the terminal list for current input char + has more than 1 terminal, the scanner code gets the current parser state from core parser (through compilerContext), + and then checks the terminals agains the ExpectedTerms set in the parser state. + As you might see in Grammar Explorer, the conflict is resolved successfully. + */ + + //Terminals + var lineNumber = new NumberLiteral("LINE_NUMBER", NumberOptions.IntOnly); + var fileNumber = new NumberLiteral("FILE_NUMBER", NumberOptions.IntOnly); + + var number = new NumberLiteral("NUMBER", NumberOptions.AllowStartEndDot); + //ints that are too long for int32 are converted to int64 + number.DefaultIntTypes = new TypeCode[] {TypeCode.Int32, TypeCode.Int64}; + number.AddExponentSymbols("eE", TypeCode.Single); + number.AddExponentSymbols("dD", TypeCode.Double); + number.AddSuffix("!", TypeCode.Single); + number.AddSuffix("#", TypeCode.Double); + + var variable = new IdentifierTerminal("Identifier"); + variable.AddSuffix("$", TypeCode.String); + variable.AddSuffix("%", TypeCode.Int32); + variable.AddSuffix("!", TypeCode.Single); + variable.AddSuffix("#", TypeCode.Double); + + var stringLiteral = new StringLiteral("STRING", "\"", StringOptions.None); + //Important: do not add comment term to base.NonGrammarTerminals list - we do use this terminal in grammar rules + var userFunctionName = variable; + var comment = new CommentTerminal("Comment", "REM", "\n"); + var short_comment = new CommentTerminal("ShortComment", "'", "\n"); + var line_cont = new LineContinuationTerminal("LineContinuation", "_"); + var comma = ToTerm(",", "comma"); + var colon = ToTerm(":", "colon"); + + var comma_opt = new NonTerminal("comma_opt"); + comma_opt.Rule = Empty | ","; + var semi_opt = new NonTerminal("semi_opt"); + semi_opt.Rule = Empty | ";"; + var pound_opt = new NonTerminal("pound_opt"); + pound_opt.Rule = Empty | "#"; + + // Non-terminals + var PROGRAM = new NonTerminal("PROGRAM"); + var LINE = new NonTerminal("LINE"); + var LINE_CONTENT_OPT = new NonTerminal("LINE_CONTENT_OPT"); + var COMMENT_OPT = new NonTerminal("COMMENT_OPT"); + var STATEMENT_LIST = new NonTerminal("STATEMENT_LIST"); + var STATEMENT = new NonTerminal("STATEMENT"); + var PRINT_STMT = new NonTerminal("PRINT_STMT"); + var PRINT_LIST = new NonTerminal("PRINT_LIST"); + var PRINT_ARG = new NonTerminal("PRINT_ARG"); + var OPEN_STMT = new NonTerminal("OPEN_STMT"); + var OPEN_STMT_MODE = new NonTerminal("OPEN_STMT_MODE"); + var OPEN_STMT_ACCESS = new NonTerminal("OPEN_STMT_ACCESS"); + var CLOSE_STMT = new NonTerminal("CLOSE_STMT"); + var INPUT_STMT = new NonTerminal("INPUT_STMT"); + var VARIABLES = new NonTerminal("VARIABLES"); + var IF_STMT = new NonTerminal("IF_STMT"); + var THEN_CLAUSE = new NonTerminal("THEN_CLAUSE"); + var ELSE_CLAUSE_OPT = new NonTerminal("ELSE_CLAUSE_OPT"); //, typeof(AstNode)); + var EXPR = new NonTerminal("EXPRESSION"); + var EXPR_LIST = new NonTerminal("EXPRESSION_LIST"); + var BINARY_OP = new NonTerminal("BINARY_OP", "operator"); + var BINARY_EXPR = new NonTerminal("BINARY_EXPR"); + var UNARY_EXPR = new NonTerminal("UNARY_EXPR"); + var SIGN = new NonTerminal("SIGN"); + var ASSIGN_STMT = new NonTerminal("ASSIGN_STMT"); + var FOR_STMT = new NonTerminal("FOR_STMT"); + var STEP_OPT = new NonTerminal("STEP_OPT"); + var NEXT_STMT = new NonTerminal("NEXT_STMT"); + var LOCATE_STMT = new NonTerminal("LOCATE_STMT"); + var WHILE_STMT = new NonTerminal("WHILE_STMT"); + var WEND_STMT = new NonTerminal("WEND_STMT"); + var SWAP_STMT = new NonTerminal("SWAP_STMT"); + var FUN_CALL = new NonTerminal("FUN_CALL"); + var VARIABLE_OR_FUNCTION_EXPR = new NonTerminal("VARIABLE_OR_FUNCTION_EXPR"); + var ARG_LIST = new NonTerminal("ARG_LIST"); + var LINE_INPUT_STMT = new NonTerminal("LINE_INPUT_STMT"); + var LINE_INPUT_POUND_STMT = new NonTerminal("LINE_INPUT_POUND_STMT"); + var END_STMT = new NonTerminal("END_STMT"); + var CLS_STMT = new NonTerminal("CLS_STMT"); + var CLEAR_STMT = new NonTerminal("CLEAR_STMT"); + var DIM_STMT = new NonTerminal("DIM_STMT"); + var DEF_FN_STMT = new NonTerminal("DEF_FN_STMT"); + var GOTO_STMT = new NonTerminal("GOTO_STMT"); + var GOSUB_STMT = new NonTerminal("GOSUB_STMT"); + var RETURN_STMT = new NonTerminal("RETURN_STMT"); + var ON_STMT = new NonTerminal("ON_STMT"); + var LINE_NUMBERS = new NonTerminal("LINE_NUMBERS"); + var RANDOMIZE_STMT = new NonTerminal("RANDOMIZE_STMT"); + + // set the PROGRAM to be the root node of BASIC programs. + this.Root = PROGRAM; + + // BNF Rules + PROGRAM.Rule = MakePlusRule(PROGRAM, LINE); + + // A line can be an empty line, or it's a number followed by a statement list ended by a new-line. + LINE.Rule = NewLine | lineNumber + LINE_CONTENT_OPT + COMMENT_OPT + NewLine | SyntaxError + NewLine; + LINE.NodeCaptionTemplate = "Line #{0}"; + + // A statement list is 1 or more statements separated by the ':' character + LINE_CONTENT_OPT.Rule = Empty | IF_STMT | STATEMENT_LIST; + STATEMENT_LIST.Rule = MakePlusRule(STATEMENT_LIST, colon, STATEMENT); + COMMENT_OPT.Rule = short_comment | comment | Empty; + + // A statement can be one of a number of types + STATEMENT.Rule = ASSIGN_STMT | PRINT_STMT | INPUT_STMT | OPEN_STMT | CLOSE_STMT + | LINE_INPUT_POUND_STMT | LINE_INPUT_STMT + | LOCATE_STMT | CLS_STMT + | END_STMT | CLEAR_STMT | DIM_STMT | DEF_FN_STMT + | SWAP_STMT | RANDOMIZE_STMT + | GOSUB_STMT | RETURN_STMT | GOTO_STMT | ON_STMT + | FOR_STMT | NEXT_STMT | WHILE_STMT | WEND_STMT; + + // The different statements are defined here + PRINT_STMT.Rule = "print" + PRINT_LIST; + PRINT_LIST.Rule = MakeStarRule(PRINT_LIST, null, PRINT_ARG); + PRINT_ARG.Rule = EXPR + semi_opt; + INPUT_STMT.Rule = "input" + semi_opt + stringLiteral + ";" + VARIABLES; + OPEN_STMT.Rule = "open" + EXPR + (Empty | "for" + OPEN_STMT_MODE) + + (Empty | "access" + OPEN_STMT_ACCESS) + "as" + pound_opt + fileNumber; + OPEN_STMT_ACCESS.Rule = "read" + (Empty | "write") | "write"; + OPEN_STMT_MODE.Rule = ToTerm("o") | "i" | "a" | "output" | "input" | "append"; + CLOSE_STMT.Rule = "close" + pound_opt + fileNumber; + LINE_INPUT_STMT.Rule = ToTerm("line") + "input" + semi_opt + stringLiteral + ";" + VARIABLE_OR_FUNCTION_EXPR; + LINE_INPUT_POUND_STMT.Rule = ToTerm("line") + "input" + ToTerm("#") + fileNumber + comma + VARIABLE_OR_FUNCTION_EXPR; + DIM_STMT.Rule = "dim" + VARIABLES; + DEF_FN_STMT.Rule = "def" + userFunctionName + (Empty | "(" + ARG_LIST + ")") + "=" + EXPR; + VARIABLES.Rule = VARIABLE_OR_FUNCTION_EXPR | VARIABLE_OR_FUNCTION_EXPR + "," + VARIABLES; + + IF_STMT.Rule = "if" + EXPR + THEN_CLAUSE + ELSE_CLAUSE_OPT; + THEN_CLAUSE.Rule = "then" + STATEMENT_LIST | GOTO_STMT; + + //Inject PreferShift hint here to explicitly set shift as preferred action. Suppresses warning message about conflict. + ELSE_CLAUSE_OPT.Rule = Empty | PreferShiftHere() + "else" + STATEMENT_LIST; + + GOTO_STMT.Rule = "goto" + lineNumber; + GOSUB_STMT.Rule = "gosub" + lineNumber; + RETURN_STMT.Rule = "return"; + ON_STMT.Rule = "on" + EXPR + (ToTerm("goto") | "gosub") + LINE_NUMBERS; + LINE_NUMBERS.Rule = MakePlusRule(LINE_NUMBERS, comma, lineNumber); + ASSIGN_STMT.Rule = VARIABLE_OR_FUNCTION_EXPR + "=" + EXPR; + LOCATE_STMT.Rule = "locate" + EXPR + comma + EXPR; + SWAP_STMT.Rule = "swap" + EXPR + comma + EXPR; + END_STMT.Rule = "end"; + CLS_STMT.Rule = "cls"; + CLEAR_STMT.Rule = ToTerm("clear") + comma + (Empty | number) + (Empty | comma + number) | "clear" + number | "clear"; + RANDOMIZE_STMT.Rule = "randomize" + EXPR; + + // An expression is a number, or a variable, a string, or the result of a binary comparison. + EXPR.Rule = number | variable | FUN_CALL | stringLiteral | BINARY_EXPR + | "(" + EXPR + ")" | UNARY_EXPR; + BINARY_EXPR.Rule = EXPR + BINARY_OP + EXPR; + UNARY_EXPR.Rule = SIGN + EXPR; + SIGN.Rule = ToTerm("-") | "+"; + + //Inject PreferShift hint here to explicitly set shift as preferred action. Suppresses warning message about conflict. + //The conflict arises from PRINT statement, when there may be ambigous interpretations for expression like + // PRINT F (X) -- either function call or identifier followed by expression + FUN_CALL.Rule = variable + PreferShiftHere() + "(" + ARG_LIST + ")"; + VARIABLE_OR_FUNCTION_EXPR.Rule = variable | FUN_CALL; + + BINARY_OP.Rule = ToTerm("+") | "^" | "-" | "*" | "/" | "=" | "<=" | ">=" | "<" | ">" | "<>" | "and" | "or"; + //let's do operator precedence right here + RegisterOperators(60, "^"); + RegisterOperators(50, "*", "/"); + RegisterOperators(40, "+", "-"); + RegisterOperators(30, "=", "<=", ">=", "<", ">", "<>"); + RegisterOperators(20, "and", "or"); + + EXPR_LIST.Rule = MakeStarRule(EXPR_LIST, EXPR); + + FOR_STMT.Rule = "for" + ASSIGN_STMT + "to" + EXPR + STEP_OPT; + STEP_OPT.Rule = Empty | "step" + EXPR; + NEXT_STMT.Rule = "next" + VARIABLES | "next"; + WHILE_STMT.Rule = "while" + EXPR; + WEND_STMT.Rule = "wend"; + + //TODO: check number of arguments for particular function in node constructor + ARG_LIST.Rule = MakePlusRule(ARG_LIST, comma, EXPR); + + + //Punctuation and Transient elements + MarkPunctuation("(", ")", ","); + MarkTransient(EXPR, STATEMENT, LINE_CONTENT_OPT, VARIABLE_OR_FUNCTION_EXPR, COMMENT_OPT); + NonGrammarTerminals.Add(line_cont); + + this.LanguageFlags = LanguageFlags.NewLineBeforeEOF; + + lineNumber.ValidateToken += identifier_ValidateToken; + + } + + void identifier_ValidateToken(object sender, ParsingEventArgs e) { + if (e.Context.CurrentToken.ValueString.Length > 4) + e.Context.CurrentToken = e.Context.CreateErrorToken("Identifier cannot be longer than 4 characters"); + }//constructor + + }//class +}//namespace diff --git a/Irony.Samples/Java/JavaGrammar.Static.cs b/Irony.Samples/Java/JavaGrammar.Static.cs new file mode 100644 index 0000000..aef4f89 --- /dev/null +++ b/Irony.Samples/Java/JavaGrammar.Static.cs @@ -0,0 +1,770 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples.Java +{ + partial class JavaGrammar + { + public static NumberLiteral CreateJavaNumber(string name) + { + var term = new NumberLiteral(name, NumberOptions.AllowStartEndDot) + { + DefaultIntTypes = new[] { TypeCode.Int32 }, + DefaultFloatType = TypeCode.Double + }; + term.AddPrefix("0x", NumberOptions.Hex); + term.AddSuffix("l", TypeCode.Int64); + term.AddSuffix("f", TypeCode.Single); + term.AddSuffix("d", TypeCode.Double); + return term; + } + + public static StringLiteral CreateJavaString(string name) + { + return new StringLiteral(name, "\"", StringOptions.AllowsAllEscapes); + } + + public static StringLiteral CreateJavaChar(string name) + { + return new StringLiteral(name, "'", StringOptions.IsChar | StringOptions.AllowsAllEscapes); + } + + public static Terminal CreateJavaNull(string name) + { + return new KeyTerm("null", name); + } + + private static void InitializeCharacterSet(ref string characterSet, IEnumerable ranges) + { + var sbCharSet = new StringBuilder(); + foreach (var range in ranges) + { + for (int i = range[0]; i <= range[1]; ++i) + { + sbCharSet.Append((Char)i); + } + } + characterSet = sbCharSet.ToString(); + } + + private static string _validIdentifierStartCharacters; + public static string ValidIdentifierStartCharacters + { + get + { + if (_validIdentifierStartCharacters == null) + { + InitializeCharacterSet(ref _validIdentifierStartCharacters, ValidIdentifierStartCharactersRanges); + } + return _validIdentifierStartCharacters; + } + } + + private static string _validIdentifierCharacters; + public static string ValidIdentifierCharacters + { + get + { + if (_validIdentifierCharacters == null) + { + InitializeCharacterSet(ref _validIdentifierCharacters, ValidIdentifierCharactersRanges); + } + return _validIdentifierCharacters; + } + } + + private static readonly int[][] ValidIdentifierStartCharactersRanges = new[] + { +#region Identifier Start Character Ranges + + new[] {36, 36}, + new[] {65, 90}, + new[] {95, 95}, + new[] {97, 122}, + new[] {162, 165}, + new[] {170, 170}, + new[] {181, 181}, + new[] {186, 186}, + new[] {192, 214}, + new[] {216, 246}, + new[] {248, 566}, + new[] {592, 705}, + new[] {710, 721}, + new[] {736, 740}, + new[] {750, 750}, + new[] {890, 890}, + new[] {902, 902}, + new[] {904, 906}, + new[] {908, 908}, + new[] {910, 929}, + new[] {931, 974}, + new[] {976, 1013}, + new[] {1015, 1019}, + new[] {1024, 1153}, + new[] {1162, 1230}, + new[] {1232, 1269}, + new[] {1272, 1273}, + new[] {1280, 1295}, + new[] {1329, 1366}, + new[] {1369, 1369}, + new[] {1377, 1415}, + new[] {1488, 1514}, + new[] {1520, 1522}, + new[] {1569, 1594}, + new[] {1600, 1610}, + new[] {1646, 1647}, + new[] {1649, 1747}, + new[] {1749, 1749}, + new[] {1765, 1766}, + new[] {1774, 1775}, + new[] {1786, 1788}, + new[] {1791, 1791}, + new[] {1808, 1808}, + new[] {1810, 1839}, + new[] {1869, 1871}, + new[] {1920, 1957}, + new[] {1969, 1969}, + new[] {2308, 2361}, + new[] {2365, 2365}, + new[] {2384, 2384}, + new[] {2392, 2401}, + new[] {2437, 2444}, + new[] {2447, 2448}, + new[] {2451, 2472}, + new[] {2474, 2480}, + new[] {2482, 2482}, + new[] {2486, 2489}, + new[] {2493, 2493}, + new[] {2524, 2525}, + new[] {2527, 2529}, + new[] {2544, 2547}, + new[] {2565, 2570}, + new[] {2575, 2576}, + new[] {2579, 2600}, + new[] {2602, 2608}, + new[] {2610, 2611}, + new[] {2613, 2614}, + new[] {2616, 2617}, + new[] {2649, 2652}, + new[] {2654, 2654}, + new[] {2674, 2676}, + new[] {2693, 2701}, + new[] {2703, 2705}, + new[] {2707, 2728}, + new[] {2730, 2736}, + new[] {2738, 2739}, + new[] {2741, 2745}, + new[] {2749, 2749}, + new[] {2768, 2768}, + new[] {2784, 2785}, + new[] {2801, 2801}, + new[] {2821, 2828}, + new[] {2831, 2832}, + new[] {2835, 2856}, + new[] {2858, 2864}, + new[] {2866, 2867}, + new[] {2869, 2873}, + new[] {2877, 2877}, + new[] {2908, 2909}, + new[] {2911, 2913}, + new[] {2929, 2929}, + new[] {2947, 2947}, + new[] {2949, 2954}, + new[] {2958, 2960}, + new[] {2962, 2965}, + new[] {2969, 2970}, + new[] {2972, 2972}, + new[] {2974, 2975}, + new[] {2979, 2980}, + new[] {2984, 2986}, + new[] {2990, 2997}, + new[] {2999, 3001}, + new[] {3065, 3065}, + new[] {3077, 3084}, + new[] {3086, 3088}, + new[] {3090, 3112}, + new[] {3114, 3123}, + new[] {3125, 3129}, + new[] {3168, 3169}, + new[] {3205, 3212}, + new[] {3214, 3216}, + new[] {3218, 3240}, + new[] {3242, 3251}, + new[] {3253, 3257}, + new[] {3261, 3261}, + new[] {3294, 3294}, + new[] {3296, 3297}, + new[] {3333, 3340}, + new[] {3342, 3344}, + new[] {3346, 3368}, + new[] {3370, 3385}, + new[] {3424, 3425}, + new[] {3461, 3478}, + new[] {3482, 3505}, + new[] {3507, 3515}, + new[] {3517, 3517}, + new[] {3520, 3526}, + new[] {3585, 3632}, + new[] {3634, 3635}, + new[] {3647, 3654}, + new[] {3713, 3714}, + new[] {3716, 3716}, + new[] {3719, 3720}, + new[] {3722, 3722}, + new[] {3725, 3725}, + new[] {3732, 3735}, + new[] {3737, 3743}, + new[] {3745, 3747}, + new[] {3749, 3749}, + new[] {3751, 3751}, + new[] {3754, 3755}, + new[] {3757, 3760}, + new[] {3762, 3763}, + new[] {3773, 3773}, + new[] {3776, 3780}, + new[] {3782, 3782}, + new[] {3804, 3805}, + new[] {3840, 3840}, + new[] {3904, 3911}, + new[] {3913, 3946}, + new[] {3976, 3979}, + new[] {4096, 4129}, + new[] {4131, 4135}, + new[] {4137, 4138}, + new[] {4176, 4181}, + new[] {4256, 4293}, + new[] {4304, 4344}, + new[] {4352, 4441}, + new[] {4447, 4514}, + new[] {4520, 4601}, + new[] {4608, 4614}, + new[] {4616, 4678}, + new[] {4680, 4680}, + new[] {4682, 4685}, + new[] {4688, 4694}, + new[] {4696, 4696}, + new[] {4698, 4701}, + new[] {4704, 4742}, + new[] {4744, 4744}, + new[] {4746, 4749}, + new[] {4752, 4782}, + new[] {4784, 4784}, + new[] {4786, 4789}, + new[] {4792, 4798}, + new[] {4800, 4800}, + new[] {4802, 4805}, + new[] {4808, 4814}, + new[] {4816, 4822}, + new[] {4824, 4846}, + new[] {4848, 4878}, + new[] {4880, 4880}, + new[] {4882, 4885}, + new[] {4888, 4894}, + new[] {4896, 4934}, + new[] {4936, 4954}, + new[] {5024, 5108}, + new[] {5121, 5740}, + new[] {5743, 5750}, + new[] {5761, 5786}, + new[] {5792, 5866}, + new[] {5870, 5872}, + new[] {5888, 5900}, + new[] {5902, 5905}, + new[] {5920, 5937}, + new[] {5952, 5969}, + new[] {5984, 5996}, + new[] {5998, 6000}, + new[] {6016, 6067}, + new[] {6103, 6103}, + new[] {6107, 6108}, + new[] {6176, 6263}, + new[] {6272, 6312}, + new[] {6400, 6428}, + new[] {6480, 6509}, + new[] {6512, 6516}, + new[] {7424, 7531}, + new[] {7680, 7835}, + new[] {7840, 7929}, + new[] {7936, 7957}, + new[] {7960, 7965}, + new[] {7968, 8005}, + new[] {8008, 8013}, + new[] {8016, 8023}, + new[] {8025, 8025}, + new[] {8027, 8027}, + new[] {8029, 8029}, + new[] {8031, 8061}, + new[] {8064, 8116}, + new[] {8118, 8124}, + new[] {8126, 8126}, + new[] {8130, 8132}, + new[] {8134, 8140}, + new[] {8144, 8147}, + new[] {8150, 8155}, + new[] {8160, 8172}, + new[] {8178, 8180}, + new[] {8182, 8188}, + new[] {8255, 8256}, + new[] {8276, 8276}, + new[] {8305, 8305}, + new[] {8319, 8319}, + new[] {8352, 8369}, + new[] {8450, 8450}, + new[] {8455, 8455}, + new[] {8458, 8467}, + new[] {8469, 8469}, + new[] {8473, 8477}, + new[] {8484, 8484}, + new[] {8486, 8486}, + new[] {8488, 8488}, + new[] {8490, 8493}, + new[] {8495, 8497}, + new[] {8499, 8505}, + new[] {8509, 8511}, + new[] {8517, 8521}, + new[] {8544, 8579}, + new[] {12293, 12295}, + new[] {12321, 12329}, + new[] {12337, 12341}, + new[] {12344, 12348}, + new[] {12353, 12438}, + new[] {12445, 12447}, + new[] {12449, 12543}, + new[] {12549, 12588}, + new[] {12593, 12686}, + new[] {12704, 12727}, + new[] {12784, 12799}, + new[] {13312, 19893}, + new[] {19968, 40869}, + new[] {40960, 42124}, + new[] {44032, 55203}, + new[] {63744, 64045}, + new[] {64048, 64106}, + new[] {64256, 64262}, + new[] {64275, 64279}, + new[] {64285, 64285}, + new[] {64287, 64296}, + new[] {64298, 64310}, + new[] {64312, 64316}, + new[] {64318, 64318}, + new[] {64320, 64321}, + new[] {64323, 64324}, + new[] {64326, 64433}, + new[] {64467, 64829}, + new[] {64848, 64911}, + new[] {64914, 64967}, + new[] {65008, 65020}, + new[] {65075, 65076}, + new[] {65101, 65103}, + new[] {65129, 65129}, + new[] {65136, 65140}, + new[] {65142, 65276}, + new[] {65284, 65284}, + new[] {65313, 65338}, + new[] {65343, 65343}, + new[] {65345, 65370}, + new[] {65381, 65470}, + new[] {65474, 65479}, + new[] {65482, 65487}, + new[] {65490, 65495}, + new[] {65498, 65500}, + new[] {65504, 65505}, + new[] {65509, 65510}, +#endregion + }; + + private static readonly int[][] ValidIdentifierCharactersRanges = new[] + { +#region Identifier Character Ranges + new[] {0, 8}, + new[] {14, 27}, + new[] {36, 36}, + new[] {48, 57}, + new[] {65, 90}, + new[] {95, 95}, + new[] {97, 122}, + new[] {127, 159}, + new[] {162, 165}, + new[] {170, 170}, + new[] {173, 173}, + new[] {181, 181}, + new[] {186, 186}, + new[] {192, 214}, + new[] {216, 246}, + new[] {248, 566}, + new[] {592, 705}, + new[] {710, 721}, + new[] {736, 740}, + new[] {750, 750}, + new[] {768, 855}, + new[] {861, 879}, + new[] {890, 890}, + new[] {902, 902}, + new[] {904, 906}, + new[] {908, 908}, + new[] {910, 929}, + new[] {931, 974}, + new[] {976, 1013}, + new[] {1015, 1019}, + new[] {1024, 1153}, + new[] {1155, 1158}, + new[] {1162, 1230}, + new[] {1232, 1269}, + new[] {1272, 1273}, + new[] {1280, 1295}, + new[] {1329, 1366}, + new[] {1369, 1369}, + new[] {1377, 1415}, + new[] {1425, 1441}, + new[] {1443, 1465}, + new[] {1467, 1469}, + new[] {1471, 1471}, + new[] {1473, 1474}, + new[] {1476, 1476}, + new[] {1488, 1514}, + new[] {1520, 1522}, + new[] {1536, 1539}, + new[] {1552, 1557}, + new[] {1569, 1594}, + new[] {1600, 1624}, + new[] {1632, 1641}, + new[] {1646, 1747}, + new[] {1749, 1757}, + new[] {1759, 1768}, + new[] {1770, 1788}, + new[] {1791, 1791}, + new[] {1807, 1866}, + new[] {1869, 1871}, + new[] {1920, 1969}, + new[] {2305, 2361}, + new[] {2364, 2381}, + new[] {2384, 2388}, + new[] {2392, 2403}, + new[] {2406, 2415}, + new[] {2433, 2435}, + new[] {2437, 2444}, + new[] {2447, 2448}, + new[] {2451, 2472}, + new[] {2474, 2480}, + new[] {2482, 2482}, + new[] {2486, 2489}, + new[] {2492, 2500}, + new[] {2503, 2504}, + new[] {2507, 2509}, + new[] {2519, 2519}, + new[] {2524, 2525}, + new[] {2527, 2531}, + new[] {2534, 2547}, + new[] {2561, 2563}, + new[] {2565, 2570}, + new[] {2575, 2576}, + new[] {2579, 2600}, + new[] {2602, 2608}, + new[] {2610, 2611}, + new[] {2613, 2614}, + new[] {2616, 2617}, + new[] {2620, 2620}, + new[] {2622, 2626}, + new[] {2631, 2632}, + new[] {2635, 2637}, + new[] {2649, 2652}, + new[] {2654, 2654}, + new[] {2662, 2676}, + new[] {2689, 2691}, + new[] {2693, 2701}, + new[] {2703, 2705}, + new[] {2707, 2728}, + new[] {2730, 2736}, + new[] {2738, 2739}, + new[] {2741, 2745}, + new[] {2748, 2757}, + new[] {2759, 2761}, + new[] {2763, 2765}, + new[] {2768, 2768}, + new[] {2784, 2787}, + new[] {2790, 2799}, + new[] {2801, 2801}, + new[] {2817, 2819}, + new[] {2821, 2828}, + new[] {2831, 2832}, + new[] {2835, 2856}, + new[] {2858, 2864}, + new[] {2866, 2867}, + new[] {2869, 2873}, + new[] {2876, 2883}, + new[] {2887, 2888}, + new[] {2891, 2893}, + new[] {2902, 2903}, + new[] {2908, 2909}, + new[] {2911, 2913}, + new[] {2918, 2927}, + new[] {2929, 2929}, + new[] {2946, 2947}, + new[] {2949, 2954}, + new[] {2958, 2960}, + new[] {2962, 2965}, + new[] {2969, 2970}, + new[] {2972, 2972}, + new[] {2974, 2975}, + new[] {2979, 2980}, + new[] {2984, 2986}, + new[] {2990, 2997}, + new[] {2999, 3001}, + new[] {3006, 3010}, + new[] {3014, 3016}, + new[] {3018, 3021}, + new[] {3031, 3031}, + new[] {3047, 3055}, + new[] {3065, 3065}, + new[] {3073, 3075}, + new[] {3077, 3084}, + new[] {3086, 3088}, + new[] {3090, 3112}, + new[] {3114, 3123}, + new[] {3125, 3129}, + new[] {3134, 3140}, + new[] {3142, 3144}, + new[] {3146, 3149}, + new[] {3157, 3158}, + new[] {3168, 3169}, + new[] {3174, 3183}, + new[] {3202, 3203}, + new[] {3205, 3212}, + new[] {3214, 3216}, + new[] {3218, 3240}, + new[] {3242, 3251}, + new[] {3253, 3257}, + new[] {3260, 3268}, + new[] {3270, 3272}, + new[] {3274, 3277}, + new[] {3285, 3286}, + new[] {3294, 3294}, + new[] {3296, 3297}, + new[] {3302, 3311}, + new[] {3330, 3331}, + new[] {3333, 3340}, + new[] {3342, 3344}, + new[] {3346, 3368}, + new[] {3370, 3385}, + new[] {3390, 3395}, + new[] {3398, 3400}, + new[] {3402, 3405}, + new[] {3415, 3415}, + new[] {3424, 3425}, + new[] {3430, 3439}, + new[] {3458, 3459}, + new[] {3461, 3478}, + new[] {3482, 3505}, + new[] {3507, 3515}, + new[] {3517, 3517}, + new[] {3520, 3526}, + new[] {3530, 3530}, + new[] {3535, 3540}, + new[] {3542, 3542}, + new[] {3544, 3551}, + new[] {3570, 3571}, + new[] {3585, 3642}, + new[] {3647, 3662}, + new[] {3664, 3673}, + new[] {3713, 3714}, + new[] {3716, 3716}, + new[] {3719, 3720}, + new[] {3722, 3722}, + new[] {3725, 3725}, + new[] {3732, 3735}, + new[] {3737, 3743}, + new[] {3745, 3747}, + new[] {3749, 3749}, + new[] {3751, 3751}, + new[] {3754, 3755}, + new[] {3757, 3769}, + new[] {3771, 3773}, + new[] {3776, 3780}, + new[] {3782, 3782}, + new[] {3784, 3789}, + new[] {3792, 3801}, + new[] {3804, 3805}, + new[] {3840, 3840}, + new[] {3864, 3865}, + new[] {3872, 3881}, + new[] {3893, 3893}, + new[] {3895, 3895}, + new[] {3897, 3897}, + new[] {3902, 3911}, + new[] {3913, 3946}, + new[] {3953, 3972}, + new[] {3974, 3979}, + new[] {3984, 3991}, + new[] {3993, 4028}, + new[] {4038, 4038}, + new[] {4096, 4129}, + new[] {4131, 4135}, + new[] {4137, 4138}, + new[] {4140, 4146}, + new[] {4150, 4153}, + new[] {4160, 4169}, + new[] {4176, 4185}, + new[] {4256, 4293}, + new[] {4304, 4344}, + new[] {4352, 4441}, + new[] {4447, 4514}, + new[] {4520, 4601}, + new[] {4608, 4614}, + new[] {4616, 4678}, + new[] {4680, 4680}, + new[] {4682, 4685}, + new[] {4688, 4694}, + new[] {4696, 4696}, + new[] {4698, 4701}, + new[] {4704, 4742}, + new[] {4744, 4744}, + new[] {4746, 4749}, + new[] {4752, 4782}, + new[] {4784, 4784}, + new[] {4786, 4789}, + new[] {4792, 4798}, + new[] {4800, 4800}, + new[] {4802, 4805}, + new[] {4808, 4814}, + new[] {4816, 4822}, + new[] {4824, 4846}, + new[] {4848, 4878}, + new[] {4880, 4880}, + new[] {4882, 4885}, + new[] {4888, 4894}, + new[] {4896, 4934}, + new[] {4936, 4954}, + new[] {4969, 4977}, + new[] {5024, 5108}, + new[] {5121, 5740}, + new[] {5743, 5750}, + new[] {5761, 5786}, + new[] {5792, 5866}, + new[] {5870, 5872}, + new[] {5888, 5900}, + new[] {5902, 5908}, + new[] {5920, 5940}, + new[] {5952, 5971}, + new[] {5984, 5996}, + new[] {5998, 6000}, + new[] {6002, 6003}, + new[] {6016, 6099}, + new[] {6103, 6103}, + new[] {6107, 6109}, + new[] {6112, 6121}, + new[] {6155, 6157}, + new[] {6160, 6169}, + new[] {6176, 6263}, + new[] {6272, 6313}, + new[] {6400, 6428}, + new[] {6432, 6443}, + new[] {6448, 6459}, + new[] {6470, 6509}, + new[] {6512, 6516}, + new[] {7424, 7531}, + new[] {7680, 7835}, + new[] {7840, 7929}, + new[] {7936, 7957}, + new[] {7960, 7965}, + new[] {7968, 8005}, + new[] {8008, 8013}, + new[] {8016, 8023}, + new[] {8025, 8025}, + new[] {8027, 8027}, + new[] {8029, 8029}, + new[] {8031, 8061}, + new[] {8064, 8116}, + new[] {8118, 8124}, + new[] {8126, 8126}, + new[] {8130, 8132}, + new[] {8134, 8140}, + new[] {8144, 8147}, + new[] {8150, 8155}, + new[] {8160, 8172}, + new[] {8178, 8180}, + new[] {8182, 8188}, + new[] {8204, 8207}, + new[] {8234, 8238}, + new[] {8255, 8256}, + new[] {8276, 8276}, + new[] {8288, 8291}, + new[] {8298, 8303}, + new[] {8305, 8305}, + new[] {8319, 8319}, + new[] {8352, 8369}, + new[] {8400, 8412}, + new[] {8417, 8417}, + new[] {8421, 8426}, + new[] {8450, 8450}, + new[] {8455, 8455}, + new[] {8458, 8467}, + new[] {8469, 8469}, + new[] {8473, 8477}, + new[] {8484, 8484}, + new[] {8486, 8486}, + new[] {8488, 8488}, + new[] {8490, 8493}, + new[] {8495, 8497}, + new[] {8499, 8505}, + new[] {8509, 8511}, + new[] {8517, 8521}, + new[] {8544, 8579}, + new[] {12293, 12295}, + new[] {12321, 12335}, + new[] {12337, 12341}, + new[] {12344, 12348}, + new[] {12353, 12438}, + new[] {12441, 12442}, + new[] {12445, 12447}, + new[] {12449, 12543}, + new[] {12549, 12588}, + new[] {12593, 12686}, + new[] {12704, 12727}, + new[] {12784, 12799}, + new[] {13312, 19893}, + new[] {19968, 40869}, + new[] {40960, 42124}, + new[] {44032, 55203}, + new[] {63744, 64045}, + new[] {64048, 64106}, + new[] {64256, 64262}, + new[] {64275, 64279}, + new[] {64285, 64296}, + new[] {64298, 64310}, + new[] {64312, 64316}, + new[] {64318, 64318}, + new[] {64320, 64321}, + new[] {64323, 64324}, + new[] {64326, 64433}, + new[] {64467, 64829}, + new[] {64848, 64911}, + new[] {64914, 64967}, + new[] {65008, 65020}, + new[] {65024, 65039}, + new[] {65056, 65059}, + new[] {65075, 65076}, + new[] {65101, 65103}, + new[] {65129, 65129}, + new[] {65136, 65140}, + new[] {65142, 65276}, + new[] {65279, 65279}, + new[] {65284, 65284}, + new[] {65296, 65305}, + new[] {65313, 65338}, + new[] {65343, 65343}, + new[] {65345, 65370}, + new[] {65381, 65470}, + new[] {65474, 65479}, + new[] {65482, 65487}, + new[] {65490, 65495}, + new[] {65498, 65500}, + new[] {65504, 65505}, + new[] {65509, 65510}, + new[] {65529, 65531}, +#endregion + }; + + } +} diff --git a/Irony.Samples/Java/JavaGrammar.Syntax.cs b/Irony.Samples/Java/JavaGrammar.Syntax.cs new file mode 100644 index 0000000..69707b8 --- /dev/null +++ b/Irony.Samples/Java/JavaGrammar.Syntax.cs @@ -0,0 +1,800 @@ +using Irony.Parsing; + +namespace Irony.Samples.Java +{ + partial class JavaGrammar + { + private void InitializeSyntax() + { + bool enableAutomaticConflictResolution = true; //Roman: moved it here and made var instead of const to get rid of compiler warnings + +#region Identifier and Literals + #pragma warning disable 168 + #pragma warning disable 162 + // ReSharper disable InconsistentNaming + // ReSharper disable ConditionIsAlwaysTrueOrFalse + + var identifier_raw = new IdentifierTerminal("_identifier_") + { + AllFirstChars = ValidIdentifierStartCharacters, + AllChars = ValidIdentifierCharacters + }; + var identifier = new NonTerminal("identifier") + { + Rule = enableAutomaticConflictResolution + ? PreferShiftHere() + identifier_raw + : identifier_raw + }; + + var number_literal = CreateJavaNumber("number"); + var character_literal = CreateJavaChar("char"); + var string_literal = CreateJavaString("string"); + var null_literal = CreateJavaNull("null"); + + // ReSharper restore ConditionIsAlwaysTrueOrFalse + // ReSharper restore InconsistentNaming + #pragma warning restore 162 + #pragma warning restore 168 +#endregion + +#region Terminals + #pragma warning disable 168 + // ReSharper disable InconsistentNaming + + var ABSTRACT = ToTerm("abstract", "abstract"); + var AMP = ToTerm("&", "amp"); + var AMP_AMP = ToTerm("&&", "amp_amp"); + var AMP_ASSIGN = ToTerm("&=", "amp_assign"); + var ASSERT = ToTerm("assert", "assert"); + var ASSIGN = ToTerm("=", "assign"); + var AT = ToTerm("@", "at"); + var BAR = ToTerm("|", "bar"); + var BAR_ASSIGN = ToTerm("|=", "bar_assign"); + var BAR_BAR = ToTerm("||", "bar_bar"); + var BOOLEAN = ToTerm("boolean", "boolean"); + var BREAK = ToTerm("break", "break"); + var BYTE = ToTerm("byte", "byte"); + var CARET = ToTerm("^", "caret"); + var CARET_ASSIGN = ToTerm("^=", "caret_assign"); + var CASE = ToTerm("case", "case"); + var CATCH = ToTerm("catch", "catch"); + var CHAR = ToTerm("char", "char"); + var CLASS_TOKEN = ToTerm("class", "class_token"); + var COLON = ToTerm(":", "colon"); + var COMMA = ToTerm(",", "comma"); + var CONST = ToTerm("const", "const"); + var CONTINUE = ToTerm("continue", "continue"); + var DEFAULT = ToTerm("default", "default"); + var DO = ToTerm("do", "do"); + var DOT_DOT_DOT = ToTerm("...", "dot_dot_dot"); + var DOUBLE = ToTerm("double", "double"); + var ELSE = ToTerm("else", "else"); + var EMARK = ToTerm("!", "emark"); + var ENUM = ToTerm("enum", "enum"); + var EQ = ToTerm("==", "eq"); + var EXTENDS = ToTerm("extends", "extends"); + var FALSE = ToTerm("false", "false"); + var FINAL = ToTerm("final", "final"); + var FINALLY_TOKEN = ToTerm("finally", "finally_token"); + var FLOAT = ToTerm("float", "float"); + var FOR = ToTerm("for", "for"); + var GOTO = ToTerm("goto", "goto"); + var GT = ToTerm(">", "gt"); + var GTEQ = ToTerm(">=", "gteq"); + var IF = ToTerm("if", "if"); + var IMPLEMENTS = ToTerm("implements", "implements"); + var IMPORT = ToTerm("import", "import"); + var INSTANCEOF = ToTerm("instanceof", "instanceof"); + var INT = ToTerm("int", "int"); + var INTERFACE = ToTerm("interface", "interface"); + var L_BRC = ToTerm("{", "l_brc"); + var LONG = ToTerm("long", "long"); + var LTEQ = ToTerm("<=", "lteq"); + var MINUS = ToTerm("-", "minus"); + var MINUS_ASSIGN = ToTerm("-=", "minus_assign"); + var MINUS_MINUS = ToTerm("--", "minus_minus"); + var NATIVE = ToTerm("native", "native"); + var NEQ = ToTerm("!=", "neq"); + var NEW = ToTerm("new", "new"); + var PACKAGE = ToTerm("package", "package"); + var PERCENT = ToTerm("%", "percent"); + var PERCENT_ASSIGN = ToTerm("%=", "percent_assign"); + var PLUS = ToTerm("+", "plus"); + var PLUS_ASSIGN = ToTerm("+=", "plus_assign"); + var PLUS_PLUS = ToTerm("++", "plus_plus"); + var PRIVATE = ToTerm("private", "private"); + var PROTECTED = ToTerm("protected", "protected"); + var PUBLIC = ToTerm("public", "public"); + var QMARK = ToTerm("?", "qmark"); + var R_BKT = ToTerm("]", "r_bkt"); + var R_BRC = ToTerm("}", "r_brc"); + var R_PAR = ToTerm(")", "r_par"); + var RETURN = ToTerm("return", "return"); + var SEMI = ToTerm(";", "semi"); + var SHL = ToTerm("<<", "shl"); + var SHL_ASSIGN = ToTerm("<<=", "shl_assign"); + var SHORT = ToTerm("short", "short"); + var SHR = ToTerm(">>", "shr"); + var SHR_ASSIGN = ToTerm(">>=", "shr_assign"); + var SLASH = ToTerm("/", "slash"); + var SLASH_ASSIGN = ToTerm("/=", "slash_assign"); + var STAR = ToTerm("*", "star"); + var STAR_ASSIGN = ToTerm("*=", "star_assign"); + var STATIC = ToTerm("static", "static"); + var STRICTFP = ToTerm("strictfp", "strictfp"); + var SWITCH = ToTerm("switch", "switch"); + var SYNCHRONIZED = ToTerm("synchronized", "synchronized"); + var THROW = ToTerm("throw", "throw"); + var THROWS_TOKEN = ToTerm("throws", "throws_token"); + var TILDE = ToTerm("~", "tilde"); + var TRANSIENT = ToTerm("transient", "transient"); + var TRUE = ToTerm("true", "true"); + var TRY = ToTerm("try", "try"); + var USHR = ToTerm(">>>", "ushr"); + var USHR_ASSIGN = ToTerm(">>>=", "ushr_assign"); + var VOID = ToTerm("void", "void"); + var VOLATILE = ToTerm("volatile", "volatile"); + var WHILE = ToTerm("while", "while"); + + #region Terminals with conflicts + #pragma warning disable 162 + // ReSharper disable ConditionIsAlwaysTrueOrFalse + + var THIS_RAW = ToTerm("this", "this"); + var THIS = new NonTerminal("_this_") + { + Rule = enableAutomaticConflictResolution + ? PreferShiftHere() + THIS_RAW + : THIS_RAW + }; + + var LT_RAW = ToTerm("<", "lt"); + var LT = new NonTerminal("_<_") + { + Rule = CustomActionHere(ResolveConflicts) + LT_RAW + }; + + var L_PAR_RAW = ToTerm("(", "l_par"); + var L_PAR = new NonTerminal("_(_") + { + Rule = enableAutomaticConflictResolution + ? PreferShiftHere() + L_PAR_RAW + : L_PAR_RAW + }; + + var L_BKT_RAW = ToTerm("[", "l_bkt"); + var L_BKT = new NonTerminal("_[_") + { + Rule = enableAutomaticConflictResolution + ? PreferShiftHere() + L_BKT_RAW + : L_BKT_RAW + }; + + var DOT_RAW = ToTerm(".", "dot"); + var DOT = new NonTerminal("_._") + { + Rule = CustomActionHere(ResolveConflicts) + DOT_RAW + }; + var SUPER_TOKEN_RAW = ToTerm("super", "super_token"); + var SUPER_TOKEN = new NonTerminal("_super_") + { + Rule = CustomActionHere(ResolveConflicts) + SUPER_TOKEN_RAW + }; + + // ReSharper restore ConditionIsAlwaysTrueOrFalse + #pragma warning restore 162 + #endregion + + // ReSharper restore InconsistentNaming + #pragma warning restore 168 +#endregion + +#region NonTerminal Declarations + // ReSharper disable InconsistentNaming + var abstract_method_declaration = new NonTerminal("abstract_method_declaration"); + var annotation = new NonTerminal("annotation"); + var annotation_declaration = new NonTerminal("annotation_declaration"); + var annotation_type_body = new NonTerminal("annotation_type_body"); + var annotation_type_element_declaration = new NonTerminal("annotation_type_element_declaration"); + var annotation_type_element_rest = new NonTerminal("annotation_type_element_rest"); + var annotations = new NonTerminal("annotations"); + var argument_list = new NonTerminal("argument_list"); + var arguments = new NonTerminal("arguments"); + var arguments_opt = new NonTerminal("arguments?"); + var array_access = new NonTerminal("array_access"); + var array_creator_rest = new NonTerminal("array_creator_rest"); + var array_initializer = new NonTerminal("array_initializer"); + var assignment_expression = new NonTerminal("assignment_expression"); + var assignment_operator = new NonTerminal("assignment_operator"); + var base_type_declaration = new NonTerminal("type_declaration_without_modifiers"); + var binary_expression = new NonTerminal("binary_expression"); + var block = new NonTerminal("block"); + var block_statement = new NonTerminal("block_statement"); + var block_statements = new NonTerminal("block_statements"); + var boolean_literal = new NonTerminal("boolean_literal"); + var cast_expression = new NonTerminal("cast_expression"); + var catch_clause = new NonTerminal("catch_clause"); + var catches = new NonTerminal("catches"); + var class_body = new NonTerminal("class_body"); + var class_body_declaration = new NonTerminal("class_body_declaration"); + var class_body_opt = new NonTerminal("class_body_opt"); + var class_creator_rest = new NonTerminal("class_creator_rest"); + var class_declaration = new NonTerminal("class_declaration"); + var class_member_declaration = new NonTerminal("class_member_declaration"); + var compilation_unit = new NonTerminal("compilation_unit"); + var constant_declaration = new NonTerminal("constant_declaration"); + var constructor_body = new NonTerminal("constructor_body"); + var constructor_declaration = new NonTerminal("constructor_declaration"); + var created_name = new NonTerminal("created_name"); + var creator = new NonTerminal("creator"); + var dim = new NonTerminal("dim"); + var dim_expr = new NonTerminal("dim_expr"); + var dim_exprs = new NonTerminal("dims_exprs"); + var dims = new NonTerminal("dims"); + var element_value = new NonTerminal("element_value"); + var element_value_array_initializer = new NonTerminal("element_value_array_initializer"); + var element_value_pair = new NonTerminal("element_value_pair"); + var element_value_pairs = new NonTerminal("element_value_pairs"); + var element_values = new NonTerminal("element_values"); + var enum_body = new NonTerminal("enum_body"); + var enum_body_declarations = new NonTerminal("enum_body_declarations"); + var enum_body_declarations_opt = new NonTerminal("enum_body_declarations?"); + var enum_constant = new NonTerminal("enum_constant"); + var enum_constants = new NonTerminal("enum_constants"); + var enum_declaration = new NonTerminal("enum_declaration"); + var exception_type_list = new NonTerminal("exception_type_list"); + var explicit_constructor_invocation = new NonTerminal("explicit_constructor_invocation"); + var explicit_generic_invocation = new NonTerminal("explicit_generic_invocation"); + var explicit_generic_invocation_suffix = new NonTerminal("explicit_generic_invocation_suffix"); + var expression = new NonTerminal("expression"); + var expression_in_parens = new NonTerminal("expression_in_parens"); + var field_access = new NonTerminal("field_access"); + var field_declaration = new NonTerminal("field_declaration"); + var for_control = new NonTerminal("for_control"); + var for_init = new NonTerminal("for_init"); + var for_update = new NonTerminal("for_update"); + var for_var_control = new NonTerminal("for_var_control"); + var formal_parameter = new NonTerminal("formal_parameter"); + var formal_parameter_list = new NonTerminal("formal_parameter_list"); + var formal_parameter_list_opt = new NonTerminal("formal_parameter_list?"); + var formal_parameters = new NonTerminal("formal_parameters"); + var identifier_suffix = new NonTerminal("identifier_suffix"); + var identifier_suffix_opt = new NonTerminal("identifier_suffix_opt"); + var import_declaration = new NonTerminal("import_declaration"); + var import_declarations = new NonTerminal("import_declarations"); + var import_wildcard = new NonTerminal("import_wildcard"); + var infix_operator = new NonTerminal("infix_operator"); + var inner_creator = new NonTerminal("inner_creator"); + var instance_initializer = new NonTerminal("instance_initializer"); + var interface_body = new NonTerminal("interface_body"); + var interface_declaration = new NonTerminal("interface_declaration"); + var interface_member_declaration = new NonTerminal("interface_member_declaration"); + var interface_type = new NonTerminal("interface_type"); + var interface_type_list = new NonTerminal("interface_type_list"); + var interfaces = new NonTerminal("interfaces"); + var interfaces_opt = new NonTerminal("interfaces?"); + var last_formal_parameter = new NonTerminal("last_formal_parameter"); + var left_hand_side = new NonTerminal("left_hand_side"); + var literal = new NonTerminal("literal"); + var local_variable_declaration = new NonTerminal("local_variable_declaration_statement"); + var method_body = new NonTerminal("method_body"); + var method_declaration = new NonTerminal("method_declaration"); + var method_declarator = new NonTerminal("method_declarator"); + var method_invocation = new NonTerminal("method_invocation"); + var modifier = new NonTerminal("modifier"); + var modifiers = new NonTerminal("modifiers"); + var modifiers_opt = new NonTerminal("modifiers?"); + var normal_import_declaration = new NonTerminal("normal_import_declaration"); + var package_declaration = new NonTerminal("package_declaration"); + var package_declaration_w_modifiers = new NonTerminal("package_declaration"); + var postfix_operator = new NonTerminal("postfix_operator"); + var prefix_operator = new NonTerminal("prefix_operator"); + var primary_expression = new NonTerminal("primary_expression"); + var primary_expression_no_new = new NonTerminal("primary_expression_no_new"); + var primitive_type = new NonTerminal("primitive_type"); + var qualified_name = new NonTerminal("qualified_name"); + var selector = new NonTerminal("selector"); + var statement = new NonTerminal("statement"); + var statement_expression = new NonTerminal("statement_expression"); + var static_import_declaration = new NonTerminal("static_import_declaration"); + var static_initializer = new NonTerminal("static_initializer"); + var super = new NonTerminal("super"); + var super_opt = new NonTerminal("super?"); + var super_suffix = new NonTerminal("super_suffix"); + var switch_block_statement_group = new NonTerminal("switch_block_statement_group"); + var switch_block_statement_groups = new NonTerminal("switch_block_statement_groups"); + var switch_label = new NonTerminal("switch_label"); + var templated_identifier = new NonTerminal("templated_identifier"); + var templated_qualified_name = new NonTerminal("templated_qualified_name"); + var throws = new NonTerminal("throws"); + var throws_opt = new NonTerminal("throws?"); + var trinary_expression = new NonTerminal("trinary_expression"); + var type = new NonTerminal("type"); + var type_argument = new NonTerminal("type_argument"); + var type_argument_list = new NonTerminal("type_argument_list"); + var type_arguments = new NonTerminal("type_arguments"); + var type_arguments_opt = new NonTerminal("type_arguments?"); + var type_bound = new NonTerminal("type_bound"); + var type_bound_list = new NonTerminal("type_bound_list"); + var type_bound_opt = new NonTerminal("type_bound?"); + var type_declaration = new NonTerminal("type_declaration"); + var type_declaration_w_modifiers = new NonTerminal("type_declaration_with_modifiers"); + var type_declarations = new NonTerminal("type_declarations"); + var type_parameter = new NonTerminal("type_parameter"); + var type_parameter_list = new NonTerminal("type_parameter_list"); + var type_parameters = new NonTerminal("type_parameters"); + var type_parameters_opt = new NonTerminal("type_parameters?"); + var unary_expression = new NonTerminal("unary_expression"); + var variable_declarator = new NonTerminal("variable_declarator"); + var variable_declarators = new NonTerminal("variable_declarators"); + var variable_declarators_rest = new NonTerminal("variable_declarators_rest"); + var variable_initializer = new NonTerminal("variable_initializer"); + var variable_initializers = new NonTerminal("variable_initializers"); + // ReSharper restore InconsistentNaming +#endregion + +#region NonTerminal Rules + + #region common + modifiers_opt.Rule = Empty | modifiers; + modifiers.Rule = MakePlusRule(modifiers, modifier); + modifier.Rule = ABSTRACT | FINAL | NATIVE | PRIVATE | PROTECTED | PUBLIC | STATIC | STRICTFP | SYNCHRONIZED | TRANSIENT | VOLATILE | annotation; + qualified_name.Rule = MakePlusRule(qualified_name, DOT, identifier); + type.Rule = templated_qualified_name; + type.Rule |= templated_qualified_name + dim + dims; + type.Rule |= primitive_type; + type.Rule |= primitive_type + dim + dims; + + super_opt.Rule = Empty | super; + super.Rule = EXTENDS + templated_qualified_name; + + interfaces_opt.Rule = Empty | interfaces; + interfaces.Rule = IMPLEMENTS + interface_type_list; + interface_type_list.Rule = MakePlusRule(interface_type_list, COMMA, interface_type); + interface_type.Rule = templated_qualified_name; + + templated_identifier.Rule = identifier + type_arguments; + templated_identifier.Rule |= identifier; + templated_qualified_name.Rule = MakePlusRule(templated_qualified_name, DOT, templated_identifier); + + type_arguments_opt.Rule = Empty | type_arguments; + type_arguments.Rule = LT + type_argument_list + GT; + type_argument_list.Rule = MakePlusRule(type_argument_list, COMMA, type_argument); + type_argument.Rule = type; + type_argument.Rule |= QMARK + EXTENDS + type; + type_argument.Rule |= QMARK + SUPER_TOKEN + type; + type_argument.Rule |= QMARK; + + type_parameters_opt.Rule = Empty | type_parameters; + type_parameters.Rule = LT + type_parameter_list + GT; + type_parameter_list.Rule = MakePlusRule(type_parameter_list, COMMA, type_parameter); + type_parameter.Rule = type; + type_parameter.Rule |= type + type_bound; + type_bound_opt.Rule = Empty | type_bound; + type_bound.Rule = EXTENDS + type_bound_list; + type_bound_list.Rule = MakePlusRule(type_bound_list, AMP, interface_type); + + dims.Rule = MakeStarRule(dims, dim); + dim.Rule = L_BKT + R_BKT; + + dim_exprs.Rule = MakePlusRule(dim_exprs, dim_expr); + dim_expr.Rule = L_BKT + expression + R_BKT; + + method_declarator.Rule = identifier + L_PAR + formal_parameter_list_opt + R_PAR + dims; + throws_opt.Rule = Empty | throws; + throws.Rule = THROWS_TOKEN + exception_type_list; + exception_type_list.Rule = MakePlusRule(exception_type_list, COMMA, templated_qualified_name); + + formal_parameter_list_opt.Rule = Empty | formal_parameter_list; + formal_parameter_list.Rule = formal_parameters + COMMA + last_formal_parameter; + formal_parameter_list.Rule |= last_formal_parameter; + formal_parameters.Rule = MakePlusRule(formal_parameters, COMMA, formal_parameter); + formal_parameter.Rule = modifiers_opt + primitive_type + dims + identifier + dims; + formal_parameter.Rule |= modifiers_opt + templated_qualified_name + dims + identifier + dims; + last_formal_parameter.Rule = formal_parameter; + last_formal_parameter.Rule |= modifiers_opt + primitive_type + dims + DOT_DOT_DOT + identifier + dims; + last_formal_parameter.Rule |= modifiers_opt + templated_qualified_name + dims + DOT_DOT_DOT + identifier + dims; + + variable_declarators_rest.Rule = MakeStarRule(variable_declarators_rest, COMMA + variable_declarator); + variable_declarators.Rule = MakePlusRule(variable_declarators, COMMA, variable_declarator); + variable_declarator.Rule = templated_qualified_name + dims; + variable_declarator.Rule |= templated_qualified_name + dims + ASSIGN + variable_initializer; + + //variable_initializers.Rule = MakeStarRule(variable_initializers, COMMA, variable_initializer, TermListOptions.AllowTrailingDelimiter); + variable_initializers.Rule = MakeListRule(variable_initializers, COMMA, variable_initializer, TermListOptions.StarList | TermListOptions.AllowTrailingDelimiter); + variable_initializer.Rule = array_initializer; + variable_initializer.Rule |= expression; + + array_initializer.Rule = L_BRC + variable_initializers + R_BRC; + array_initializer.Rule |= L_BRC + COMMA + R_BRC; + + primitive_type.Rule = BOOLEAN; + primitive_type.Rule |= BYTE; + primitive_type.Rule |= SHORT; + primitive_type.Rule |= INT; + primitive_type.Rule |= LONG; + primitive_type.Rule |= CHAR; + primitive_type.Rule |= FLOAT; + primitive_type.Rule |= DOUBLE; + + literal.Rule = null_literal; + literal.Rule |= number_literal; + literal.Rule |= character_literal; + literal.Rule |= string_literal; + literal.Rule |= boolean_literal; + + boolean_literal.Rule = TRUE | FALSE; + + statement_expression.Rule = expression; + + block.Rule = L_BRC + block_statements + R_BRC; + block_statements.Rule = MakeStarRule(block_statements, block_statement); + block_statement.Rule = statement; + block_statement.Rule |= explicit_constructor_invocation; + block_statement.Rule |= modifiers + local_variable_declaration; + block_statement.Rule |= local_variable_declaration; + block_statement.Rule |= modifiers + annotation_declaration; + block_statement.Rule |= annotation_declaration; + block_statement.Rule |= modifiers + class_declaration; + block_statement.Rule |= class_declaration; + block_statement.Rule |= modifiers + enum_declaration; + block_statement.Rule |= enum_declaration; + block_statement.Rule |= modifiers + interface_declaration; + block_statement.Rule |= interface_declaration; + + local_variable_declaration.Rule = primitive_type + dim + dims + variable_declarators + SEMI; + local_variable_declaration.Rule |= primitive_type + variable_declarators + SEMI; + local_variable_declaration.Rule |= templated_qualified_name + dim + dims + variable_declarators + SEMI; + local_variable_declaration.Rule |= templated_qualified_name + variable_declarators + SEMI; + #endregion + + #region top level + compilation_unit.Rule = Empty; + compilation_unit.Rule |= package_declaration_w_modifiers + import_declarations + type_declarations; + compilation_unit.Rule |= package_declaration + import_declarations + type_declarations; + compilation_unit.Rule |= import_declarations + type_declarations; + compilation_unit.Rule |= package_declaration_w_modifiers + type_declarations; + compilation_unit.Rule |= package_declaration + type_declarations; + compilation_unit.Rule |= type_declarations; + compilation_unit.Rule |= package_declaration_w_modifiers + import_declarations; + compilation_unit.Rule |= package_declaration + import_declarations; + compilation_unit.Rule |= import_declarations; + compilation_unit.Rule |= package_declaration_w_modifiers; + compilation_unit.Rule |= package_declaration; + + package_declaration.Rule = PACKAGE + qualified_name + SEMI; + package_declaration_w_modifiers.Rule = modifiers + PACKAGE + qualified_name + SEMI; + + import_declarations.Rule = MakePlusRule(import_declarations, import_declaration); + import_declaration.Rule = normal_import_declaration | static_import_declaration; + normal_import_declaration.Rule = IMPORT + qualified_name + import_wildcard + SEMI; + static_import_declaration.Rule = IMPORT + STATIC + qualified_name + import_wildcard + SEMI; + import_wildcard.Rule = Empty | (DOT + STAR); + + type_declarations.Rule = MakePlusRule(type_declarations, type_declaration); + type_declaration.Rule = type_declaration_w_modifiers | base_type_declaration | SEMI; + + type_declaration_w_modifiers.Rule = modifiers + base_type_declaration; + base_type_declaration.Rule = annotation_declaration | class_declaration | enum_declaration | interface_declaration; + #endregion + + #region type declarations + annotation_declaration.Rule = AT + INTERFACE + identifier + L_BRC + annotation_type_body + R_BRC; + class_declaration.Rule = CLASS_TOKEN + identifier + type_parameters_opt + super_opt + interfaces_opt + L_BRC + class_body + R_BRC; + enum_declaration.Rule = ENUM + identifier + interfaces_opt + L_BRC + enum_body + R_BRC; + interface_declaration.Rule = INTERFACE + identifier + type_parameters_opt + EXTENDS + interface_type_list + L_BRC + interface_body + R_BRC; + interface_declaration.Rule |= INTERFACE + identifier + type_parameters_opt + L_BRC + interface_body + R_BRC; + #endregion + + #region interface_declaration + interface_body.Rule = MakeStarRule(interface_body, interface_member_declaration); + interface_member_declaration.Rule = SEMI; + interface_member_declaration.Rule |= modifiers_opt + constant_declaration; + interface_member_declaration.Rule |= modifiers_opt + abstract_method_declaration; + interface_member_declaration.Rule |= modifiers_opt + class_declaration; + interface_member_declaration.Rule |= modifiers_opt + interface_declaration; + + // these type_parameters are not actually allowed but it saves a whole lot of work in resolving code. + constant_declaration.Rule = type_parameters_opt + primitive_type + dims + variable_declarators + SEMI; + constant_declaration.Rule |= type_parameters_opt + templated_qualified_name + dims + variable_declarators + SEMI; + + abstract_method_declaration.Rule = type_parameters_opt + primitive_type + dims + method_declarator + throws_opt + SEMI; + abstract_method_declaration.Rule |= type_parameters_opt + templated_qualified_name + dims + method_declarator + throws_opt + SEMI; + abstract_method_declaration.Rule |= type_parameters_opt + VOID + dims + method_declarator + throws_opt + SEMI; + #endregion + + #region class_declaration + class_body_opt.Rule = Empty | L_BRC + class_body + R_BRC; + class_body.Rule = MakeStarRule(class_body, class_body_declaration); + + class_body_declaration.Rule = class_member_declaration; + class_body_declaration.Rule |= instance_initializer; + class_body_declaration.Rule |= static_initializer; + class_body_declaration.Rule |= constructor_declaration; + + class_member_declaration.Rule = SEMI; + class_member_declaration.Rule |= modifiers + field_declaration; + class_member_declaration.Rule |= field_declaration; + class_member_declaration.Rule |= modifiers + method_declaration; + class_member_declaration.Rule |= method_declaration; + class_member_declaration.Rule |= modifiers + annotation_declaration; + class_member_declaration.Rule |= annotation_declaration; + class_member_declaration.Rule |= modifiers + class_declaration; + class_member_declaration.Rule |= class_declaration; + class_member_declaration.Rule |= modifiers + enum_declaration; + class_member_declaration.Rule |= enum_declaration; + class_member_declaration.Rule |= modifiers + interface_declaration; + class_member_declaration.Rule |= interface_declaration; + + // these type_parameters are not actually allowed but it saves a whole lot of work in resolving code. + field_declaration.Rule = type_parameters_opt + primitive_type + dims + variable_declarators + SEMI; + field_declaration.Rule |= type_parameters_opt + templated_qualified_name + dims + variable_declarators + SEMI; + + method_declaration.Rule = type_parameters_opt + VOID + method_declarator + throws_opt + method_body; + method_declaration.Rule |= type_parameters_opt + primitive_type + dims + method_declarator + throws_opt + method_body; + method_declaration.Rule |= type_parameters_opt + templated_qualified_name + dims + method_declarator + throws_opt + method_body; + + method_body.Rule = SEMI; + method_body.Rule |= block; + + instance_initializer.Rule = block; + static_initializer.Rule = STATIC + block; + + constructor_declaration.Rule = modifiers + type_parameters_opt + identifier + L_PAR + formal_parameter_list_opt + R_PAR + throws_opt + constructor_body; + constructor_declaration.Rule |= type_parameters_opt + identifier + L_PAR + formal_parameter_list_opt + R_PAR + throws_opt + constructor_body; + + constructor_body.Rule = L_BRC + block_statements + R_BRC; + + explicit_constructor_invocation.Rule = type_arguments_opt + THIS + arguments + SEMI; + explicit_constructor_invocation.Rule |= type_arguments_opt + SUPER_TOKEN + arguments + SEMI; + #endregion + + #region annotations + //element_value_pairs.Rule = MakeStarRule(element_value_pairs, COMMA, element_value_pair, TermListOptions.AllowTrailingDelimiter); + element_value_pairs.Rule = MakeListRule(element_value_pairs, COMMA, element_value_pair, TermListOptions.StarList | TermListOptions.AllowTrailingDelimiter); + element_value_pair.Rule = identifier + ASSIGN + element_value; + element_value_array_initializer.Rule = L_BRC + element_values + R_BRC; + //element_values.Rule = MakeStarRule(element_values, COMMA, element_value, TermListOptions.AllowTrailingDelimiter); + element_values.Rule = MakeListRule(element_values, COMMA, element_value, TermListOptions.StarList | TermListOptions.AllowTrailingDelimiter); + element_value.Rule = annotation; + element_value.Rule |= element_value_array_initializer; + element_value.Rule |= expression; + + annotations.Rule = MakeStarRule(annotations, annotation); + annotation.Rule = AT + qualified_name + L_PAR + element_value_pair + R_PAR; + annotation.Rule |= AT + qualified_name + L_PAR + element_value_pair + COMMA + element_value_pairs + R_PAR; + annotation.Rule |= AT + qualified_name + L_PAR + element_values + R_PAR; + annotation.Rule |= AT + qualified_name; + + + annotation_type_body.Rule = MakeStarRule(annotation_type_body, annotation_type_element_declaration); + annotation_type_element_declaration.Rule = modifiers + annotation_type_element_rest; + annotation_type_element_declaration.Rule |= annotation_type_element_rest; + + annotation_type_element_rest.Rule = type + identifier + L_PAR + R_PAR + DEFAULT + element_value + SEMI; + annotation_type_element_rest.Rule |= type + identifier + L_PAR + R_PAR + SEMI; + annotation_type_element_rest.Rule |= type + variable_declarators + SEMI; + annotation_type_element_rest.Rule |= class_declaration; + annotation_type_element_rest.Rule |= enum_declaration; + annotation_type_element_rest.Rule |= annotation_declaration; + #endregion + + #region enum + enum_body.Rule = enum_constants + enum_body_declarations_opt; + enum_body.Rule |= COMMA + enum_body_declarations_opt; + //enum_constants.Rule = MakeStarRule(enum_constants, COMMA, enum_constant, TermListOptions.AllowTrailingDelimiter); + enum_constants.Rule = MakeListRule(enum_constants, COMMA, enum_constant, TermListOptions.StarList | TermListOptions.AllowTrailingDelimiter); + enum_constant.Rule = annotations + identifier; + enum_constant.Rule |= annotations + identifier + arguments; + enum_constant.Rule |= annotations + identifier + arguments + L_BRC + class_body + R_BRC; + enum_constant.Rule |= annotations + identifier + L_BRC + class_body + R_BRC; + + arguments_opt.Rule = Empty | arguments; + arguments.Rule = L_PAR + argument_list + R_PAR; + argument_list.Rule = MakeStarRule(argument_list, COMMA, expression); + + enum_body_declarations_opt.Rule = Empty | enum_body_declarations; + enum_body_declarations.Rule = SEMI + class_body; + #endregion + + #region expressions + super_suffix.Rule = arguments; + super_suffix.Rule |= DOT + identifier + arguments_opt; + + explicit_generic_invocation_suffix.Rule = SUPER_TOKEN + super_suffix; + explicit_generic_invocation_suffix.Rule |= identifier + arguments; + + array_creator_rest.Rule = dim + dims + array_initializer; + array_creator_rest.Rule |= dim_exprs + dim + dims; + array_creator_rest.Rule |= dim_exprs; + + class_creator_rest.Rule = arguments + class_body_opt; + + creator.Rule = type_arguments_opt + created_name + (array_creator_rest | class_creator_rest); + created_name.Rule = templated_qualified_name; + + explicit_generic_invocation.Rule = type_arguments + PreferShiftHere() + explicit_generic_invocation_suffix; + + inner_creator.Rule = templated_qualified_name + class_creator_rest; + + identifier_suffix_opt.Rule = Empty | identifier_suffix; + identifier_suffix.Rule = dim + dims + DOT + CLASS_TOKEN; + identifier_suffix.Rule = arguments; + identifier_suffix.Rule = DOT + CLASS_TOKEN; + identifier_suffix.Rule |= DOT + explicit_generic_invocation; + identifier_suffix.Rule |= DOT + THIS; + identifier_suffix.Rule |= DOT + type_arguments_opt + SUPER_TOKEN + arguments; + identifier_suffix.Rule |= DOT + NEW + type_arguments_opt + inner_creator; + + selector.Rule = DOT + explicit_generic_invocation; + selector.Rule |= DOT + THIS; + selector.Rule |= DOT + type_arguments_opt + SUPER_TOKEN + arguments; + selector.Rule |= DOT + NEW + type_arguments_opt + inner_creator; + selector.Rule |= L_BKT + expression + R_BKT; + + primary_expression.Rule = primary_expression_no_new; + primary_expression.Rule |= NEW + creator; + + expression_in_parens.Rule = L_PAR + expression + R_PAR; + expression_in_parens.Rule |= L_PAR + templated_qualified_name + R_PAR; + + cast_expression.Rule = L_PAR + templated_qualified_name + R_PAR + expression; + cast_expression.Rule |= L_PAR + templated_qualified_name + dim + dims + R_PAR + expression; + cast_expression.Rule |= L_PAR + primitive_type + R_PAR + expression; + cast_expression.Rule |= L_PAR + primitive_type + dim + dims + R_PAR + expression; + + primary_expression_no_new.Rule = expression_in_parens; + primary_expression_no_new.Rule |= cast_expression; + primary_expression_no_new.Rule |= THIS; + primary_expression_no_new.Rule |= literal; + primary_expression_no_new.Rule |= templated_qualified_name + dim + dims + identifier_suffix; + primary_expression_no_new.Rule |= templated_qualified_name + dim + dims; + primary_expression_no_new.Rule |= templated_qualified_name + identifier_suffix; + primary_expression_no_new.Rule |= templated_qualified_name; + primary_expression_no_new.Rule |= primitive_type + dims + DOT + CLASS_TOKEN; + primary_expression_no_new.Rule |= VOID + DOT + CLASS_TOKEN; + primary_expression_no_new.Rule |= array_access; + primary_expression_no_new.Rule |= field_access; + primary_expression_no_new.Rule |= method_invocation; + + method_invocation.Rule = expression + arguments; + method_invocation.Rule |= expression + DOT + type_arguments_opt + identifier + arguments; + method_invocation.Rule |= type_arguments_opt + SUPER_TOKEN + DOT + type_arguments_opt + identifier + arguments; + method_invocation.Rule |= expression + DOT + type_arguments_opt + SUPER_TOKEN + DOT + type_arguments_opt + identifier + arguments; + method_invocation.Rule |= type_arguments_opt + THIS + DOT + type_arguments_opt + identifier + arguments; + method_invocation.Rule |= expression + DOT + type_arguments_opt + THIS + DOT + type_arguments_opt + identifier + arguments; + + field_access.Rule = expression + DOT + type_arguments_opt + identifier; + field_access.Rule |= type_arguments_opt + SUPER_TOKEN + DOT + type_arguments_opt + identifier; + field_access.Rule |= expression + DOT + type_arguments_opt + SUPER_TOKEN + DOT + type_arguments_opt + identifier; + field_access.Rule |= type_arguments_opt + THIS + DOT + type_arguments_opt + identifier; + field_access.Rule |= expression + DOT + type_arguments_opt + THIS + DOT + type_arguments_opt + identifier; + + assignment_operator.Rule = ASSIGN | PLUS_ASSIGN | MINUS_ASSIGN | STAR_ASSIGN | SLASH_ASSIGN | AMP_ASSIGN | + BAR_ASSIGN | CARET_ASSIGN | PERCENT_ASSIGN | SHL_ASSIGN | SHR_ASSIGN | USHR_ASSIGN ; + + + infix_operator.Rule = BAR_BAR | AMP_AMP | BAR | AMP | CARET | EQ | NEQ | LT | GT | LTEQ | GTEQ | SHR | SHL | USHR | + PLUS | MINUS | STAR | SLASH | PERCENT | INSTANCEOF; + + prefix_operator.Rule = PLUS_PLUS | MINUS_MINUS | EMARK | TILDE | PLUS | MINUS; + postfix_operator.Rule = PLUS_PLUS | MINUS_MINUS; + + array_access.Rule = templated_qualified_name + dim_expr; + array_access.Rule |= primary_expression_no_new + dim_expr; + array_access.Rule |= array_access + dim_expr; + + left_hand_side.Rule = templated_qualified_name; + left_hand_side.Rule |= expression_in_parens; + left_hand_side.Rule |= field_access; + left_hand_side.Rule |= array_access; + + unary_expression.Rule = prefix_operator + expression; + unary_expression.Rule |= expression + selector + postfix_operator; + unary_expression.Rule |= expression + selector; + unary_expression.Rule |= expression + postfix_operator; + + binary_expression.Rule = expression + infix_operator + expression; + trinary_expression.Rule = expression + QMARK + expression + COLON + expression; + + assignment_expression.Rule = left_hand_side + assignment_operator + expression; + + expression.Rule = primary_expression; + expression.Rule |= assignment_expression; + expression.Rule |= unary_expression; + expression.Rule |= binary_expression; + expression.Rule |= trinary_expression; + #endregion + + #region statements + statement.Rule = block; + statement.Rule |= ASSERT + expression + SEMI; + statement.Rule |= ASSERT + expression + COLON + expression + SEMI; + statement.Rule |= IF + L_PAR + expression + R_PAR + statement; + statement.Rule |= IF + L_PAR + expression + R_PAR + statement + PreferShiftHere() + ELSE + statement; + statement.Rule |= FOR + L_PAR + for_control + R_PAR + statement; + statement.Rule |= WHILE + L_PAR + expression + R_PAR + statement; + statement.Rule |= DO + statement + WHILE + L_PAR + expression + R_PAR + SEMI; + statement.Rule |= TRY + block + catches + FINALLY_TOKEN + block; + statement.Rule |= TRY + block + catches; + statement.Rule |= TRY + block + FINALLY_TOKEN + block; + statement.Rule |= SWITCH + L_PAR + expression + R_PAR + L_BRC + switch_block_statement_groups + R_BRC; + statement.Rule |= SYNCHRONIZED + L_PAR + expression + R_PAR + block; + statement.Rule |= RETURN + expression + SEMI; + statement.Rule |= RETURN + SEMI; + statement.Rule |= THROW + expression + SEMI; + statement.Rule |= BREAK + identifier + SEMI; + statement.Rule |= BREAK + SEMI; + statement.Rule |= CONTINUE + identifier + SEMI; + statement.Rule |= CONTINUE + SEMI; + statement.Rule |= SEMI; + statement.Rule |= statement_expression + SEMI; + statement.Rule |= identifier + COLON + statement; + + for_control.Rule = for_var_control; + for_control.Rule |= for_init + SEMI + expression + SEMI + for_update; + for_control.Rule |= for_init + SEMI + expression + SEMI; + for_control.Rule |= for_init + SEMI + SEMI + for_update; + for_control.Rule |= for_init + SEMI + SEMI; + + for_var_control.Rule = modifiers + type + identifier + COLON + expression; + for_var_control.Rule |= type + identifier + COLON + expression; + + for_init.Rule = for_update; + for_init.Rule |= modifiers + type + variable_declarators; + for_init.Rule |= type + variable_declarators; + + for_update.Rule = MakePlusRule(for_update, COMMA, statement_expression); + + catches.Rule = MakePlusRule(catches, catch_clause); + catch_clause.Rule = CATCH + L_PAR + formal_parameter + R_PAR + block; + + switch_block_statement_groups.Rule = MakeStarRule(switch_block_statement_groups, switch_block_statement_group); + switch_block_statement_group.Rule = switch_label + block_statements; + switch_label.Rule = CASE + expression + COLON; + switch_label.Rule |= DEFAULT + COLON; + #endregion + + #region operator precedence + RegisterOperators(1, Associativity.Right, ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN, STAR_ASSIGN, SLASH_ASSIGN, AMP_ASSIGN, BAR_ASSIGN, CARET_ASSIGN, PERCENT_ASSIGN, SHL_ASSIGN, SHR_ASSIGN, USHR_ASSIGN); + RegisterOperators(2, Associativity.Right, QMARK); + RegisterOperators(3, Associativity.Left, BAR_BAR); + RegisterOperators(4, Associativity.Left, AMP_AMP); + RegisterOperators(5, Associativity.Left, BAR); + RegisterOperators(6, Associativity.Left, CARET); + RegisterOperators(7, Associativity.Left, AMP); + RegisterOperators(8, Associativity.Left, EQ, NEQ); + RegisterOperators(9, Associativity.Left, INSTANCEOF, GT, GTEQ, LT, LTEQ); + RegisterOperators(10, Associativity.Left, SHL, SHR, USHR); + RegisterOperators(11, Associativity.Left, PLUS, MINUS); + RegisterOperators(12, Associativity.Left, STAR, SLASH, PERCENT); + RegisterOperators(13, Associativity.Right, PLUS_PLUS, MINUS_MINUS, TILDE, EMARK, NEW); + RegisterOperators(14, Associativity.Left, DOT); + RegisterOperators(15, Associativity.Neutral, R_PAR, R_BKT); + #endregion +#endregion + + Root = compilation_unit; + + mSkipTokensInPreview.UnionWith(new Terminal[] { identifier_raw, DOT_RAW, COMMA, L_BKT_RAW, R_BKT, QMARK }); + MarkTransient( + #region Transients + type_parameters_opt, + super_opt, + interfaces_opt, + type_arguments_opt, + type_bound_opt, + type_declaration, + import_declaration, + modifier, + modifiers_opt, + enum_body_declarations_opt, + expression, + L_BKT, + L_PAR, + LT + #endregion + ); + } + } +} diff --git a/Irony.Samples/Java/JavaGrammar.cs b/Irony.Samples/Java/JavaGrammar.cs new file mode 100644 index 0000000..ad879f8 --- /dev/null +++ b/Irony.Samples/Java/JavaGrammar.cs @@ -0,0 +1,131 @@ +using Irony.Parsing; + +namespace Irony.Samples.Java +{ + [Language("Java", "1.6", "An (almost) complete java parser")] + public partial class JavaGrammar : Grammar + { + private readonly TerminalSet mSkipTokensInPreview = new TerminalSet(); //used in token preview for conflict resolution + + public JavaGrammar() + { + GrammarComments = "NOTE: This grammar does not parse hex floating point literals."; + + var singleLineComment = new CommentTerminal("SingleLineComment", "//", "\r", "\n", "\u2085", "\u2028", "\u2029"); + var delimitedComment = new CommentTerminal("DelimitedComment", "/*", "*/"); + NonGrammarTerminals.Add(singleLineComment); + NonGrammarTerminals.Add(delimitedComment); + + MarkPunctuation(";", ",", "(", ")", "{", "}", "[", "]", ":", "@"); + + InitializeSyntax(); + } + + public override void OnScannerSelectTerminal(ParsingContext context) + { + if (context.Source.PreviewChar == '>' && context.Status == ParserStatus.Previewing) + { + context.CurrentTerminals.Clear(); + context.CurrentTerminals.Add(ToTerm(">", "gt")); //select the ">" terminal + } + base.OnScannerSelectTerminal(context); + } + + private void ResolveConflicts(ParsingContext context, CustomParserAction action) { + } + /* BROKEN + public override void OnResolvingConflict(ConflictResolutionArgs args) + { + switch (args.Context.CurrentParserInput.Term.Name) + { + case "[": + { + args.Scanner.BeginPreview(); + var preview = args.Scanner.GetToken(); + string previewSym = preview.Terminal.Name; + args.Result = previewSym == "]" ? PreferredActionType.Reduce : PreferredActionType.Shift; + args.Scanner.EndPreview(true); + return; + } + case "dot": + { + args.Scanner.BeginPreview(); + var preview = args.Scanner.GetToken(); + string previewSym = preview.Text; + if (previewSym == "<") + { + // skip over any type arguments + int depth = 0; + do + { + if (previewSym == "<") + { + ++depth; + } + else if (previewSym == ">") + { + --depth; + } + preview = args.Scanner.GetToken(); + previewSym = preview.Text; + } while (depth > 0 && preview.Terminal != Eof); + } + + switch (previewSym) + { + case "new": + case "super": + case "this": + args.Result = PreferredActionType.Reduce; + break; + default: + args.Result = PreferredActionType.Shift; + break; + } + args.Scanner.EndPreview(true); + return; + } + case "lt": + { + args.Scanner.BeginPreview(); + int ltCount = 0; + string previewSym; + while (true) + { + //Find first token ahead (using preview mode) that is either end of generic parameter (">") or something else + Token preview; + do + { + preview = args.Scanner.GetToken(); + } while (mSkipTokensInPreview.Contains(preview.Terminal) && preview.Terminal != Eof); + //See what did we find + previewSym = preview.Terminal.Name; + if ((previewSym == "<") || (previewSym == "lt")) + { + ltCount++; + } + else if (((previewSym == ">") || (previewSym == "gt")) && ltCount > 0) + { + ltCount--; + continue; + } + else + break; + } + //if we see ">", then it is type argument, not operator + if ((previewSym == ">") || (previewSym == "gt")) + { + args.Result = PreferredActionType.Shift; + } + else + { + args.Result = PreferredActionType.Reduce; + } + args.Scanner.EndPreview(true); + //keep previewed tokens; important to keep ">>" matched to two ">" symbols, not one combined symbol (see method below) + return; + } + } + } */ + } +} diff --git a/Irony.Samples/Java/about_java_grammar.txt b/Irony.Samples/Java/about_java_grammar.txt new file mode 100644 index 0000000..70bc690 --- /dev/null +++ b/Irony.Samples/Java/about_java_grammar.txt @@ -0,0 +1,6 @@ +Disclaimer: +This grammar was contributed by codeplex user JereJones - our thanks go to him. +I might disagree with some minor design decisions he made here, but mostly this is the question of style +and personal preferences. Just to make sure you understand this is not coming from me (Irony coordinator), +so please address your questions about this grammar to the author. +Roman Ivantsov diff --git a/Irony.Samples/MiniPython/MiniPython.cs b/Irony.Samples/MiniPython/MiniPython.cs new file mode 100644 index 0000000..215fa33 --- /dev/null +++ b/Irony.Samples/MiniPython/MiniPython.cs @@ -0,0 +1,141 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; +using Irony.Interpreter; +using Irony.Interpreter.Ast; + +namespace Irony.Samples.MiniPython { + // The grammar for a very small subset of Python. This is work in progress, + // I will be adding more features as we go along, bringing it closer to real python. + // Current version: expressions, assignments, indented code blocks, function defs, function calls + // Full support for Python line joining rules: line continuation symbol "\", automatic line joining when + // line ends in the middle of expression, with unbalanced parenthesis + // Python is important test case for Irony as an indentation-sensitive language. + + [Language("MiniPython", "0.2", "Micro-subset of Python, work in progress")] + public class MiniPythonGrammar : InterpretedLanguageGrammar { + public MiniPythonGrammar() : base(caseSensitive: true) { + + // 1. Terminals + var number = TerminalFactory.CreatePythonNumber("number"); + var identifier = TerminalFactory.CreatePythonIdentifier("identifier"); + var comment = new CommentTerminal("comment", "#", "\n", "\r"); + //comment must to be added to NonGrammarTerminals list; it is not used directly in grammar rules, + // so we add it to this list to let Scanner know that it is also a valid terminal. + base.NonGrammarTerminals.Add(comment); + var comma = ToTerm(","); + var colon = ToTerm(":"); + + // 2. Non-terminals + var Expr = new NonTerminal("Expr"); + var Term = new NonTerminal("Term"); + var BinExpr = new NonTerminal("BinExpr", typeof(BinaryOperationNode)); + var ParExpr = new NonTerminal("ParExpr"); + var UnExpr = new NonTerminal("UnExpr", typeof(UnaryOperationNode)); + var UnOp = new NonTerminal("UnOp", "operator"); + var BinOp = new NonTerminal("BinOp", "operator"); + var AssignmentStmt = new NonTerminal("AssignmentStmt", typeof(AssignmentNode)); + var Stmt = new NonTerminal("Stmt"); + var ExtStmt = new NonTerminal("ExtStmt"); + //Just as a test for NotSupportedNode + var ReturnStmt = new NonTerminal("return", typeof(NotSupportedNode)); + var Block = new NonTerminal("Block"); + var StmtList = new NonTerminal("StmtList", typeof(StatementListNode)); + + var ParamList = new NonTerminal("ParamList", typeof(ParamListNode)); + var ArgList = new NonTerminal("ArgList", typeof(ExpressionListNode)); + var FunctionDef = new NonTerminal("FunctionDef", typeof(FunctionDefNode)); + var FunctionCall = new NonTerminal("FunctionCall", typeof(FunctionCallNode)); + + + // 3. BNF rules + Expr.Rule = Term | UnExpr | BinExpr; + Term.Rule = number | ParExpr | identifier | FunctionCall; + ParExpr.Rule = "(" + Expr + ")"; + UnExpr.Rule = UnOp + Term; + UnOp.Rule = ToTerm("+") | "-"; + BinExpr.Rule = Expr + BinOp + Expr; + BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "**"; + AssignmentStmt.Rule = identifier + "=" + Expr; + Stmt.Rule = AssignmentStmt | Expr | ReturnStmt; + ReturnStmt.Rule = "return" + Expr; //Not supported for execution! - we associate NotSupportedNode with ReturnStmt + //Eos is End-Of-Statement token produced by CodeOutlineFilter + ExtStmt.Rule = Stmt + Eos | FunctionDef; + Block.Rule = Indent + StmtList + Dedent; + StmtList.Rule = MakePlusRule(StmtList, ExtStmt); + + ParamList.Rule = MakeStarRule(ParamList, comma, identifier); + ArgList.Rule = MakeStarRule(ArgList, comma, Expr); + FunctionDef.Rule = "def" + identifier + "(" + ParamList + ")" + colon + Eos + Block; + FunctionDef.NodeCaptionTemplate = "def #{1}(...)"; + FunctionCall.Rule = identifier + "(" + ArgList + ")"; + FunctionCall.NodeCaptionTemplate = "call #{0}(...)"; + + this.Root = StmtList; // Set grammar root + + // 4. Token filters - created in a separate method CreateTokenFilters + // we need to add continuation symbol to NonGrammarTerminals because it is not used anywhere in grammar + NonGrammarTerminals.Add(ToTerm(@"\")); + + // 5. Operators precedence + RegisterOperators(1, "+", "-"); + RegisterOperators(2, "*", "/"); + RegisterOperators(3, Associativity.Right, "**"); + + // 6. Miscellaneous: punctuation, braces, transient nodes + MarkPunctuation("(", ")", ":"); + RegisterBracePair("(", ")"); + MarkTransient(Term, Expr, Stmt, ExtStmt, UnOp, BinOp, ExtStmt, ParExpr, Block); + + // 7. Error recovery rule + ExtStmt.ErrorRule = SyntaxError + Eos; + FunctionDef.ErrorRule = SyntaxError + Dedent; + + // 8. Syntax error reporting + AddToNoReportGroup("("); + AddToNoReportGroup(Eos); + AddOperatorReportGroup("operator"); + + // 9. Initialize console attributes + ConsoleTitle = "Mini-Python Console"; + ConsoleGreeting = +@"Irony Sample Console for mini-Python. + + Supports a small sub-set of Python: assignments, arithmetic operators, + function declarations with 'def'. Supports big integer arithmetics. + Supports Python indentation and line-joining rules, including '\' as + a line joining symbol. + +Press Ctrl-C to exit the program at any time. +"; + ConsolePrompt = ">>>"; + ConsolePromptMoreInput = "..."; + + // 10. Language flags + this.LanguageFlags = LanguageFlags.NewLineBeforeEOF | LanguageFlags.CreateAst | LanguageFlags.SupportsBigInt; + + }//constructor + + public override void CreateTokenFilters(LanguageData language, TokenFilterList filters) { + var outlineFilter = new CodeOutlineFilter(language.GrammarData, + OutlineOptions.ProduceIndents | OutlineOptions.CheckBraces, ToTerm(@"\")); // "\" is continuation symbol + filters.Add(outlineFilter); + } + + }//class +}//namespace + + diff --git a/Irony.Samples/Properties/AssemblyInfo.cs b/Irony.Samples/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dea2f74 --- /dev/null +++ b/Irony.Samples/Properties/AssemblyInfo.cs @@ -0,0 +1,47 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Irony.Samples")] +[assembly: AssemblyDescription("Sample grammars for Irony Framework")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Irony Samples")] +[assembly: AssemblyCopyright("Copyright © 2010 Roman Ivantsov")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1c979943-3796-4801-bb92-8d6cfbe50a63")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Irony.Samples/SQL/Sql89-GoldParser.txt b/Irony.Samples/SQL/Sql89-GoldParser.txt new file mode 100644 index 0000000..15c7699 --- /dev/null +++ b/Irony.Samples/SQL/Sql89-GoldParser.txt @@ -0,0 +1,259 @@ +! ----------------------------------------------------------------------------------- +! SQL '89 +! +! SQL (Structured Query Language) +! +! The SQL programming language was developed as a uniform means of modifying and +! querying relational databases. By using a single abstract language to interact +! with the database, programs can be written that are independent of the vender and +! format of the database itself. Variations are used by Oracle, Microsoft and most +! other developers +! +! In 1992, a new version SQL as released but has yet to be implemented by any major +! developer. The reason to this lies in the sheer complexity of the grammar. SQL 92 +! contains over 300 rules and a myraid of new features. For instance, in SQL 92, the +! developer can create types using COBOL syntax rather than the normal data types of +! SQL 89. This reason combined with the fact that SQL 89 is a time-tested and +! ample tool maintains it as the standard of the database industry. +! +! Update: +! 02/17/2005 +! Added "NULL" to the rule, I also added more comments to the grammar +! +! Note: This is an ad hoc version of the language. If there are any flaws, please +! visit www.devincook.com/goldparser +! ----------------------------------------------------------------------------------- + +"Name" = 'SQL 89' +"Version" = '1989' +"About" = 'This is the ANSI 89 version of SQL. Variations are used by' + | 'Oracle, Microsoft and most other database developers' + +"Start Symbol" = + +! ============================================================================= +! Comments +! ============================================================================= + +Comment Start = '/*' +Comment End = '*/' +Comment Line = '--' + +! ============================================================================= +! Terminals +! ============================================================================= + +{String Ch 1} = {Printable} - ["] +{String Ch 2} = {Printable} - [''] +{Id Ch Standard} = {Alphanumeric} + [_] +{Id Ch Extended} = {Printable} - ['['] - [']'] + +StringLiteral = '"'{String Ch 1}*'"' | ''{String Ch 2}*'' +IntegerLiteral = {Digit}+ +RealLiteral = {Digit}+'.'{Digit}+ + +!----- Identifiers in SQL are very complex. + +Id = ({Letter}{Id Ch Standard}* | '['{Id Ch Extended}+']') ('.'({Letter}{Id Ch Standard}* | '['{Id Ch Extended}+']'))? + +! ============================================================================= +! Rules +! ============================================================================= + + ::= + | + | + | + | + | + | INSERT INTO Id '(' ')' VALUES '(' ')' + + ::= UPDATE Id SET + + ::= Id '=' ',' + | Id '=' + + ::= DELETE FROM Id + +! ============================================================================= +! Select Statement +! ============================================================================= + + ')' + | '(' ')' + + ::= ',' + | + + ::= ',' + | + + ::= Id + | Id Id diff --git a/Irony.Samples/SQL/SqlGrammar.cs b/Irony.Samples/SQL/SqlGrammar.cs new file mode 100644 index 0000000..06a0917 --- /dev/null +++ b/Irony.Samples/SQL/SqlGrammar.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples.SQL { + // Loosely based on SQL89 grammar from Gold parser. Supports some extra TSQL constructs. + + [Language("SQL", "89", "SQL 89 grammar")] + public class SqlGrammar : Grammar { + public SqlGrammar() : base(false) { //SQL is case insensitive + //Terminals + var comment = new CommentTerminal("comment", "/*", "*/"); + var lineComment = new CommentTerminal("line_comment", "--", "\n", "\r\n"); + NonGrammarTerminals.Add(comment); + NonGrammarTerminals.Add(lineComment); + var number = new NumberLiteral("number"); + var string_literal = new StringLiteral("string", "'", StringOptions.AllowsDoubledQuote); + var Id_simple = TerminalFactory.CreateSqlExtIdentifier(this, "id_simple"); //covers normal identifiers (abc) and quoted id's ([abc d], "abc d") + var comma = ToTerm(","); + var dot = ToTerm("."); + var CREATE = ToTerm("CREATE"); + var NULL = ToTerm("NULL"); + var NOT = ToTerm("NOT"); + var UNIQUE = ToTerm("UNIQUE"); + var WITH = ToTerm("WITH"); + var TABLE = ToTerm("TABLE"); + var ALTER = ToTerm("ALTER"); + var ADD = ToTerm("ADD"); + var COLUMN = ToTerm("COLUMN"); + var DROP = ToTerm("DROP"); + var CONSTRAINT = ToTerm("CONSTRAINT"); + var INDEX = ToTerm("INDEX"); + var ON = ToTerm("ON"); + var KEY = ToTerm("KEY"); + var PRIMARY = ToTerm("PRIMARY"); + var INSERT = ToTerm("INSERT"); + var INTO = ToTerm("INTO"); + var UPDATE = ToTerm("UPDATE"); + var SET = ToTerm("SET"); + var VALUES = ToTerm("VALUES"); + var DELETE = ToTerm("DELETE"); + var SELECT = ToTerm("SELECT"); + var FROM = ToTerm("FROM"); + var AS = ToTerm("AS"); + var COUNT = ToTerm("COUNT"); + var JOIN = ToTerm("JOIN"); + var BY = ToTerm("BY"); + + //Non-terminals + var Id = new NonTerminal("Id"); + var stmt = new NonTerminal("stmt"); + var createTableStmt = new NonTerminal("createTableStmt"); + var createIndexStmt = new NonTerminal("createIndexStmt"); + var alterStmt = new NonTerminal("alterStmt"); + var dropTableStmt = new NonTerminal("dropTableStmt"); + var dropIndexStmt = new NonTerminal("dropIndexStmt"); + var selectStmt = new NonTerminal("selectStmt"); + var insertStmt = new NonTerminal("insertStmt"); + var updateStmt = new NonTerminal("updateStmt"); + var deleteStmt = new NonTerminal("deleteStmt"); + var fieldDef = new NonTerminal("fieldDef"); + var fieldDefList = new NonTerminal("fieldDefList"); + var nullSpecOpt = new NonTerminal("nullSpecOpt"); + var typeName = new NonTerminal("typeName"); + var typeSpec = new NonTerminal("typeSpec"); + var typeParamsOpt = new NonTerminal("typeParams"); + var constraintDef = new NonTerminal("constraintDef"); + var constraintListOpt = new NonTerminal("constraintListOpt"); + var constraintTypeOpt = new NonTerminal("constraintTypeOpt"); + var idlist = new NonTerminal("idlist"); + var idlistPar = new NonTerminal("idlistPar"); + var uniqueOpt = new NonTerminal("uniqueOpt"); + var orderList = new NonTerminal("orderList"); + var orderMember = new NonTerminal("orderMember"); + var orderDirOpt = new NonTerminal("orderDirOpt"); + var withClauseOpt = new NonTerminal("withClauseOpt"); + var alterCmd = new NonTerminal("alterCmd"); + var insertData = new NonTerminal("insertData"); + var intoOpt = new NonTerminal("intoOpt"); + var assignList = new NonTerminal("assignList"); + var whereClauseOpt = new NonTerminal("whereClauseOpt"); + var assignment = new NonTerminal("assignment"); + var expression = new NonTerminal("expression"); + var exprList = new NonTerminal("exprList"); + var selRestrOpt = new NonTerminal("selRestrOpt"); + var selList = new NonTerminal("selList"); + var intoClauseOpt = new NonTerminal("intoClauseOpt"); + var fromClauseOpt = new NonTerminal("fromClauseOpt"); + var groupClauseOpt = new NonTerminal("groupClauseOpt"); + var havingClauseOpt = new NonTerminal("havingClauseOpt"); + var orderClauseOpt = new NonTerminal("orderClauseOpt"); + var columnItemList = new NonTerminal("columnItemList"); + var columnItem = new NonTerminal("columnItem"); + var columnSource = new NonTerminal("columnSource"); + var asOpt = new NonTerminal("asOpt"); + var aliasOpt = new NonTerminal("aliasOpt"); + var aggregate = new NonTerminal("aggregate"); + var aggregateArg = new NonTerminal("aggregateArg"); + var aggregateName = new NonTerminal("aggregateName"); + var tuple = new NonTerminal("tuple"); + var joinChainOpt = new NonTerminal("joinChainOpt"); + var joinKindOpt = new NonTerminal("joinKindOpt"); + var term = new NonTerminal("term"); + var unExpr = new NonTerminal("unExpr"); + var unOp = new NonTerminal("unOp"); + var binExpr = new NonTerminal("binExpr"); + var binOp = new NonTerminal("binOp"); + var betweenExpr = new NonTerminal("betweenExpr"); + var inExpr = new NonTerminal("inExpr"); + var parSelectStmt = new NonTerminal("parSelectStmt"); + var notOpt = new NonTerminal("notOpt"); + var funCall = new NonTerminal("funCall"); + var stmtLine = new NonTerminal("stmtLine"); + var semiOpt = new NonTerminal("semiOpt"); + var stmtList = new NonTerminal("stmtList"); + var funArgs = new NonTerminal("funArgs"); + var inStmt = new NonTerminal("inStmt"); + + //BNF Rules + this.Root = stmtList; + stmtLine.Rule = stmt + semiOpt; + semiOpt.Rule = Empty | ";"; + stmtList.Rule = MakePlusRule(stmtList, stmtLine); + + //ID + Id.Rule = MakePlusRule(Id, dot, Id_simple); + + stmt.Rule = createTableStmt | createIndexStmt | alterStmt + | dropTableStmt | dropIndexStmt + | selectStmt | insertStmt | updateStmt | deleteStmt + | "GO" ; + //Create table + createTableStmt.Rule = CREATE + TABLE + Id + "(" + fieldDefList + ")" + constraintListOpt; + fieldDefList.Rule = MakePlusRule(fieldDefList, comma, fieldDef); + fieldDef.Rule = Id + typeName + typeParamsOpt + nullSpecOpt; + nullSpecOpt.Rule = NULL | NOT + NULL | Empty; + typeName.Rule = ToTerm("BIT") | "DATE" | "TIME" | "TIMESTAMP" | "DECIMAL" | "REAL" | "FLOAT" | "SMALLINT" | "INTEGER" + | "INTERVAL" | "CHARACTER" + // MS SQL types: + | "DATETIME" | "INT" | "DOUBLE" | "CHAR" | "NCHAR" | "VARCHAR" | "NVARCHAR" + | "IMAGE" | "TEXT" | "NTEXT"; + typeParamsOpt.Rule = "(" + number + ")" | "(" + number + comma + number + ")" | Empty; + constraintDef.Rule = CONSTRAINT + Id + constraintTypeOpt; + constraintListOpt.Rule = MakeStarRule(constraintListOpt, constraintDef ); + constraintTypeOpt.Rule = PRIMARY + KEY + idlistPar | UNIQUE + idlistPar | NOT + NULL + idlistPar + | "Foreign" + KEY + idlistPar + "References" + Id + idlistPar; + idlistPar.Rule = "(" + idlist + ")"; + idlist.Rule = MakePlusRule(idlist, comma, Id); + + //Create Index + createIndexStmt.Rule = CREATE + uniqueOpt + INDEX + Id + ON + Id + orderList + withClauseOpt; + uniqueOpt.Rule = Empty | UNIQUE; + orderList.Rule = MakePlusRule(orderList, comma, orderMember); + orderMember.Rule = Id + orderDirOpt; + orderDirOpt.Rule = Empty | "ASC" | "DESC"; + withClauseOpt.Rule = Empty | WITH + PRIMARY | WITH + "Disallow" + NULL | WITH + "Ignore" + NULL; + + //Alter + alterStmt.Rule = ALTER + TABLE + Id + alterCmd; + alterCmd.Rule = ADD + COLUMN + fieldDefList + constraintListOpt + | ADD + constraintDef + | DROP + COLUMN + Id + | DROP + CONSTRAINT + Id; + + //Drop stmts + dropTableStmt.Rule = DROP + TABLE + Id; + dropIndexStmt.Rule = DROP + INDEX + Id + ON + Id; + + //Insert stmt + insertStmt.Rule = INSERT + intoOpt + Id + idlistPar + insertData; + insertData.Rule = selectStmt | VALUES + "(" + exprList + ")"; + intoOpt.Rule = Empty | INTO; //Into is optional in MSSQL + + //Update stmt + updateStmt.Rule = UPDATE + Id + SET + assignList + whereClauseOpt; + assignList.Rule = MakePlusRule(assignList, comma, assignment); + assignment.Rule = Id + "=" + expression; + + //Delete stmt + deleteStmt.Rule = DELETE + FROM + Id + whereClauseOpt; + + //Select stmt + selectStmt.Rule = SELECT + selRestrOpt + selList + intoClauseOpt + fromClauseOpt + whereClauseOpt + + groupClauseOpt + havingClauseOpt + orderClauseOpt; + selRestrOpt.Rule = Empty | "ALL" | "DISTINCT"; + selList.Rule = columnItemList | "*"; + columnItemList.Rule = MakePlusRule(columnItemList, comma, columnItem); + columnItem.Rule = columnSource + aliasOpt; + aliasOpt.Rule = Empty | asOpt + Id; + asOpt.Rule = Empty | AS; + columnSource.Rule = aggregate | Id; + aggregate.Rule = aggregateName + "(" + aggregateArg + ")"; + aggregateArg.Rule = expression | "*"; + aggregateName.Rule = COUNT | "Avg" | "Min" | "Max" | "StDev" | "StDevP" | "Sum" | "Var" | "VarP"; + intoClauseOpt.Rule = Empty | INTO + Id; + fromClauseOpt.Rule = Empty | FROM + idlist + joinChainOpt; + joinChainOpt.Rule = Empty | joinKindOpt + JOIN + idlist + ON + Id + "=" + Id; + joinKindOpt.Rule = Empty | "INNER" | "LEFT" | "RIGHT"; + whereClauseOpt.Rule = Empty | "WHERE" + expression; + groupClauseOpt.Rule = Empty | "GROUP" + BY + idlist; + havingClauseOpt.Rule = Empty | "HAVING" + expression; + orderClauseOpt.Rule = Empty | "ORDER" + BY + orderList; + + //Expression + exprList.Rule = MakePlusRule(exprList, comma, expression); + expression.Rule = term | unExpr | binExpr;// | betweenExpr; //-- BETWEEN doesn't work - yet; brings a few parsing conflicts + term.Rule = Id | string_literal | number | funCall | tuple | parSelectStmt;// | inStmt; + tuple.Rule = "(" + exprList + ")"; + parSelectStmt.Rule = "(" + selectStmt + ")"; + unExpr.Rule = unOp + term; + unOp.Rule = NOT | "+" | "-" | "~"; + binExpr.Rule = expression + binOp + expression; + binOp.Rule = ToTerm("+") | "-" | "*" | "/" | "%" //arithmetic + | "&" | "|" | "^" //bit + | "=" | ">" | "<" | ">=" | "<=" | "<>" | "!=" | "!<" | "!>" + | "AND" | "OR" | "LIKE" | NOT + "LIKE" | "IN" | NOT + "IN" ; + betweenExpr.Rule = expression + notOpt + "BETWEEN" + expression + "AND" + expression; + notOpt.Rule = Empty | NOT; + //funCall covers some psedo-operators and special forms like ANY(...), SOME(...), ALL(...), EXISTS(...), IN(...) + funCall.Rule = Id + "(" + funArgs + ")"; + funArgs.Rule = selectStmt | exprList; + inStmt.Rule = expression + "IN" + "(" + exprList + ")"; + + //Operators + RegisterOperators(10, "*", "/", "%"); + RegisterOperators(9, "+", "-"); + RegisterOperators(8, "=", ">", "<", ">=", "<=", "<>", "!=", "!<", "!>", "LIKE", "IN"); + RegisterOperators(7, "^", "&", "|"); + RegisterOperators(6, NOT); + RegisterOperators(5, "AND"); + RegisterOperators(4, "OR"); + + MarkPunctuation(",", "(", ")"); + MarkPunctuation(asOpt, semiOpt); + //Note: we cannot declare binOp as transient because it includes operators "NOT LIKE", "NOT IN" consisting of two tokens. + // Transient non-terminals cannot have more than one non-punctuation child nodes. + // Instead, we set flag InheritPrecedence on binOp , so that it inherits precedence value from it's children, and this precedence is used + // in conflict resolution when binOp node is sitting on the stack + base.MarkTransient(stmt, term, asOpt, aliasOpt, stmtLine, expression, unOp, tuple); + binOp.SetFlag(TermFlags.InheritPrecedence); + + }//constructor + + }//class +}//namespace diff --git a/Irony.Samples/SampleExpressionEvaluator/SampleExpressionEvaluatorGrammar.cs b/Irony.Samples/SampleExpressionEvaluator/SampleExpressionEvaluatorGrammar.cs new file mode 100644 index 0000000..c1e3ca0 --- /dev/null +++ b/Irony.Samples/SampleExpressionEvaluator/SampleExpressionEvaluatorGrammar.cs @@ -0,0 +1,28 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; +using Irony.Interpreter; +using Irony.Interpreter.Evaluator; + +namespace Irony.Samples { + //The purpose of this class is pure convenience - to make expression evaluator grammar (which is in Irony.Interpreter assembly) to appear + // with other sample grammars. + [Language("SampleExpressionEvaluator", "1.0", "Multi-line expression evaluator")] + public class SampleExpressionEvaluatorGrammar : ExpressionEvaluatorGrammar { } + +}//namespace + + diff --git a/Irony.Samples/Scheme/SchemeGrammar.cs b/Irony.Samples/Scheme/SchemeGrammar.cs new file mode 100644 index 0000000..a17a0d6 --- /dev/null +++ b/Irony.Samples/Scheme/SchemeGrammar.cs @@ -0,0 +1,210 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +namespace Irony.Samples.Scheme { + [Language("Scheme", "1.0", "Sample Scheme grammar")] + public class SchemeGrammar : Grammar { + // It is loosely based on R6RS specs. + // See Grammar Errors tab in GrammarExplorer for remaining conflicts. + public SchemeGrammar() { + + #region Terminals + ConstantTerminal Constant = new ConstantTerminal("Constant", typeof(LiteralValueNode)); + Constant.Add("#T", 1); + Constant.Add("#t", 1); + Constant.Add("#F", null); + Constant.Add("#f", null); + Constant.Add("'()", null); + Constant.Add(@"#\nul", '\u0000'); + Constant.Add(@"#\alarm", '\u0007'); + Constant.Add(@"#\backspace", '\b'); + Constant.Add(@"#\tab", '\t'); + Constant.Add(@"#\linefeed", '\n'); + Constant.Add(@"#\vtab", '\v'); + Constant.Add(@"#\page", '\f'); + Constant.Add(@"#\return", '\r'); + Constant.Add(@"#\esc", '\u001B'); + Constant.Add(@"#\space", ' '); + Constant.Add(@"#\delete", '\u007F'); + + // TODO: build SchemeCharLiteral + // the following is nonsense, just to put something there + var charLiteral = new StringLiteral("Char", "'", StringOptions.None); + var stringLiteral = new StringLiteral("String", "\"", StringOptions.AllowsAllEscapes); + //Identifiers. Note: added "-", just to allow IDs starting with "->" + var SimpleIdentifier = new IdentifierTerminal("SimpleIdentifier", "_+-*/.@?!<>=", "_+-*/.@?!<>=$%&:^~"); + // name extraChars extraFirstChars + var Number = TerminalFactory.CreateSchemeNumber("Number"); + var Byte = new NumberLiteral("Byte", NumberOptions.IntOnly); + + //Comments + Terminal Comment = new CommentTerminal("Comment", "#|", "|#"); + Terminal LineComment = new CommentTerminal("LineComment", ";", "\n"); + NonGrammarTerminals.Add(Comment); //add comments explicitly to this list as it is not reachable from Root + NonGrammarTerminals.Add(LineComment); + #endregion + + #region NonTerminals + var Module = new NonTerminal("Module"); + var Library = new NonTerminal("Library"); + var LibraryList = new NonTerminal("Library+"); + var Script = new NonTerminal("Script"); + + var Abbreviation = new NonTerminal("Abbreviation"); + var Vector = new NonTerminal("Vector"); + var ByteList = new NonTerminal("ByteList"); + var ByteVector = new NonTerminal("ByteVector"); + var Datum = new NonTerminal("Datum"); //Datum in R6RS terms + var DatumOpt = new NonTerminal("DatumOpt"); //Datum in R6RS terms + var DatumList = new NonTerminal("Datum+"); + var DatumListOpt = new NonTerminal("Datum*"); + var Statement = new NonTerminal("Statement"); + var Atom = new NonTerminal("Atom"); + var CompoundDatum = new NonTerminal("CompoundDatum"); + var AbbrevPrefix = new NonTerminal("AbbrevPrefix"); + + var LibraryName = new NonTerminal("LibraryName"); + var LibraryBody = new NonTerminal("LibraryBody"); + var ImportSection = new NonTerminal("ImportSection"); + var ExportSection = new NonTerminal("ExportSection"); + var ImportSpec = new NonTerminal("ImportSpec"); + var ImportSpecList = new NonTerminal("ImportSpecList"); + var ExportSpec = new NonTerminal("ExportSpec"); + var ExportSpecList = new NonTerminal("ExportSpecList"); + var LP = new NonTerminal("LP"); //"(" or "[" + var RP = new NonTerminal("RP"); // ")" or "]" + var Identifier = new NonTerminal("Identifier"); + var IdentifierList = new NonTerminal("IdentifierList"); + var IdentifierListOpt = new NonTerminal("IdentifierListOpt"); + var PeculiarIdentifier = new NonTerminal("PeculiarIdentifier"); + var LibraryVersion = new NonTerminal("LibraryVersion"); + var VersionListOpt = new NonTerminal("VersionListOpt"); + + var FunctionCall = new NonTerminal("FunctionCall", typeof(FunctionCallNode)); + var FunctionRef = new NonTerminal("FunctionRef"); //transient + var SpecialForm = new NonTerminal("SpecialForm"); //transient + var DefineVarForm = new NonTerminal("DefineVarForm", typeof(AssignmentNode)); + var DefineFunForm = new NonTerminal("DefineFunForm", typeof(FunctionDefNode)); + var LambdaForm = new NonTerminal("LambdaForm", typeof(LambdaNode)); + var IfForm = new NonTerminal("IfForm", typeof(IfNode)); + var CondForm = new NonTerminal("CondForm"); + var CondClause = new NonTerminal("CondClause"); + var CondClauseList = new NonTerminal("CondClauseList"); + var CondElseOpt = new NonTerminal("CondElseOpt"); + var BeginForm = new NonTerminal("BeginForm", typeof(StatementListNode)); + var LetForm = new NonTerminal("LetForm"); //not implemented + var LetRecForm = new NonTerminal("LetRecForm"); //not implemented + var LetPair = new NonTerminal("LetPair"); + var LetPairList = new NonTerminal("LetPairList"); + #endregion + + #region Rules + base.Root = Module; + + LP.Rule = ToTerm("(") | "["; //R6RS allows mix & match () and [] + RP.Rule = ToTerm(")") | "]"; + + // Module.Rule = LibraryListOpt + Script; -- this brings conflicts + Module.Rule = LibraryList + Script | Script; + LibraryList.Rule = MakePlusRule(LibraryList, Library); + Script.Rule = ImportSection + DatumList | DatumList; + + //Library + // the following doesn't work - brings conflicts that incorrectly resolved by default shifting + //Library.Rule = LP + "library" + LibraryName + ExportSectionOpt + ImportSectionOpt + DatumListOpt + RP; + Library.Rule = LP + "library" + LibraryName + LibraryBody + RP; + //Note - we should be using DatumListOpt, but that brings 2 conflicts, so for now it is just DatumList + //Note that the following style of BNF expressions is strongly discouraged - all productions should be of the same length, + // so that the process of mapping child nodes to parent's properties is straightforward. + LibraryBody.Rule = ExportSection + ImportSection + DatumList + | ExportSection + DatumList + | ImportSection + DatumList + | DatumList; + LibraryName.Rule = LP + IdentifierList + LibraryVersion.Q() + RP; + LibraryVersion.Rule = LP + VersionListOpt + RP; //zero or more subversion numbers + VersionListOpt.Rule = MakeStarRule(VersionListOpt, Number); + ExportSection.Rule = LP + "export" + ExportSpecList + RP; + ImportSection.Rule = LP + "import" + ImportSpecList + RP; + ExportSpecList.Rule = MakePlusRule(ExportSpecList, ExportSpec); + ImportSpecList.Rule = MakePlusRule(ImportSpecList, ImportSpec); + ExportSpec.Rule = Identifier | LP + "rename" + LP + Identifier + Identifier + RP + RP; + ImportSpec.Rule = LP + Identifier + RP; // - much more complex in R6RS + + //Datum + Datum.Rule = Atom | CompoundDatum; + DatumOpt.Rule = Empty | Datum; + DatumList.Rule = MakePlusRule(DatumList, Datum); + DatumListOpt.Rule = MakeStarRule(DatumListOpt, Datum); + Atom.Rule = Number | Identifier | stringLiteral | Constant | charLiteral | "."; + CompoundDatum.Rule = Statement | Abbreviation | Vector | ByteVector; + Identifier.Rule = SimpleIdentifier | PeculiarIdentifier; + IdentifierList.Rule = MakePlusRule(IdentifierList, Identifier); + IdentifierListOpt.Rule = MakeStarRule(IdentifierListOpt, Identifier); + + //TODO: create PeculiarIdentifier custom terminal instead of var + // or just custom SchemeIdentifier terminal + PeculiarIdentifier.Rule = ToTerm("+") | "-" | "..."; // |"->" + subsequent; (should be!) + Abbreviation.Rule = AbbrevPrefix + Datum; + AbbrevPrefix.Rule = ToTerm("'") | "`" | ",@" | "," | "#'" | "#`" | "#,@" | "#,"; + Vector.Rule = "#(" + DatumListOpt + ")"; + ByteVector.Rule = "#vu8(" + ByteList + ")"; + ByteList.Rule = MakeStarRule(ByteList, Byte); + + Statement.Rule = FunctionCall | SpecialForm; + + FunctionCall.Rule = LP + FunctionRef + DatumListOpt + RP; + FunctionRef.Rule = Identifier | Statement; + + SpecialForm.Rule = DefineVarForm | DefineFunForm | LambdaForm | IfForm | CondForm | BeginForm | LetForm | LetRecForm; + DefineVarForm.Rule = LP + "define" + Identifier + Datum + RP; + DefineVarForm.AstConfig.PartsMap = new int[] { 1, 2 }; + + DefineFunForm.Rule = LP + "define" + LP + Identifier + IdentifierListOpt + RP + DatumList + RP; + LambdaForm.Rule = LP + "lambda" + LP + IdentifierListOpt + RP + DatumList + RP; + IfForm.Rule = LP + "if" + Datum + Datum + DatumOpt + RP; + + CondForm.Rule = LP + "cond" + CondClauseList + CondElseOpt + RP; + CondClauseList.Rule = MakePlusRule(CondClauseList, CondClause); + CondClause.Rule = LP + Datum + DatumList + RP; + CondElseOpt.Rule = Empty | LP + "else" + DatumList + RP; + LetForm.Rule = LP + "let" + LP + LetPairList + RP + DatumList + RP; + LetRecForm.Rule = LP + "letrec" + LP + LetPairList + RP + DatumList + RP; + BeginForm.Rule = LP + "begin" + DatumList + RP; + LetPairList.Rule = MakePlusRule(LetPairList, LetPair); + LetPair.Rule = LP + Identifier + Datum + RP; + #endregion + + //Register brace pairs + RegisterBracePair("(", ")"); + RegisterBracePair("[", "]"); + + MarkPunctuation(LP, RP); + MarkTransient(Datum, CompoundDatum, Statement, SpecialForm, Atom, FunctionRef); + + //Scheme is tail-recursive language + base.LanguageFlags |= LanguageFlags.TailRecursive;// | LanguageFlags.CreateAst; + + }//constructor + + + }//class + + + + +}//namespace diff --git a/Irony.Samples/SourceSamples/99 bottles.bas b/Irony.Samples/SourceSamples/99 bottles.bas new file mode 100644 index 0000000..b0f0044 --- /dev/null +++ b/Irony.Samples/SourceSamples/99 bottles.bas @@ -0,0 +1,19 @@ +005 REM 99 Bottles of Beer in GW-BASIC +010 REM by Stefan Scheler - Ilmenau, Germany in June 2005 +015 REM GW-BASIC (named after Greg Whitten, an early Microsoft employee and is +020 REM known more affectionately as 'gee-whiz') was a dialect of BASIC developed +025 REM by Microsoft, originally for Compaq. It was the predecessor of QBasic. +030 CLS +035 FOR I = 99 TO 1 STEP -1 +040 MODE = 1: GOSUB 080 +045 PRINT I; "bottle" + BOTTLES$ + " of beer on the wall,"; i; "bottle" + BOTTLES$ + " of beer." +050 MODE = 2: GOSUB 080 +055 PRINT " Take one down and pass it around,"; i-1; "bottle" + BOTTLES$ + " of beer on the wall." +060 NEXT +065 PRINT " No more bottles of beer on the wall, no more bottles of beer." +070 PRINT " Go to the store and buy some more. 99 bottles of beer." +075 END + +080 REM subroutine handles plural s +085 IF I = MODE THEN BOTTLES$ = "" ELSE BOTTLES$ = "s" +090 RETURN diff --git a/Irony.Samples/SourceSamples/DataFiles/CarModels.csv b/Irony.Samples/SourceSamples/DataFiles/CarModels.csv new file mode 100644 index 0000000..6109744 --- /dev/null +++ b/Irony.Samples/SourceSamples/DataFiles/CarModels.csv @@ -0,0 +1,4 @@ +1997,Ford,E350,"ac, abs, moon",3000.00 +1999,Chevy,"Venture ""Extended Edition""","",4900.00 +1996,Jeep,Grand Cherokee,"MUST SELL! +air, moon roof, loaded",4799.00 \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/DataFiles/Sample1.json b/Irony.Samples/SourceSamples/DataFiles/Sample1.json new file mode 100644 index 0000000..d91956d --- /dev/null +++ b/Irony.Samples/SourceSamples/DataFiles/Sample1.json @@ -0,0 +1,12 @@ +{"menu": { + "id": "file", + "value": "File", + "popup": { + "menuitem": [ + {"value": "New", "onclick": "CreateNewDoc()"}, + {"value": "Open", "onclick": "OpenDoc()"}, + {"value": "Close", "onclick": "CloseDoc()"} + ] + } +}} + diff --git a/Irony.Samples/SourceSamples/DataFiles/Sample2.json b/Irony.Samples/SourceSamples/DataFiles/Sample2.json new file mode 100644 index 0000000..c016377 --- /dev/null +++ b/Irony.Samples/SourceSamples/DataFiles/Sample2.json @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/Irony.Samples/SourceSamples/DataFiles/Sample3.json b/Irony.Samples/SourceSamples/DataFiles/Sample3.json new file mode 100644 index 0000000..07e439d --- /dev/null +++ b/Irony.Samples/SourceSamples/DataFiles/Sample3.json @@ -0,0 +1,26 @@ +{"widget": { + "debug": "on", + "window": { + "title": "Sample Konfabulator Widget", + "name": "main_window", + "width": 500, + "height": 500 + }, + "image": { + "src": "Images/Sun.png", + "name": "sun1", + "hOffset": 250, + "vOffset": 250, + "alignment": "center" + }, + "text": { + "data": "Click Here", + "size": 36, + "style": "bold", + "name": "text1", + "hOffset": 250, + "vOffset": 100, + "alignment": "center", + "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;" + } +}} \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/Java/Sample 1.java b/Irony.Samples/SourceSamples/Java/Sample 1.java new file mode 100644 index 0000000..bc28991 --- /dev/null +++ b/Irony.Samples/SourceSamples/Java/Sample 1.java @@ -0,0 +1,122 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6627364 6627366 + * @summary Synthesize important classes if they are missing from the (boot)classpath + */ + +import java.io.*; +import java.util.*; + +public class Main +{ + File testSrc = new File(System.getProperty("test.src")); + + public static void main(String[] args) throws Exception { + new Main().run(); + } + + public void run() throws Exception { + + // compile with standard bootclasspath + compile(true, "Test.java"); + + // compile with various missing system classes + + List base_files = Arrays.asList( + "Boolean.java", + "Byte.java", + "Character.java", + "Integer.java", + "Long.java", + "Number.java", + "Object.java", + "Short.java", + "Void.java" + ); + + List extra_files = Arrays.asList( + "Double.java", + "Float.java", + "Cloneable.java", + "Serializable.java" + ); + + List files = new ArrayList(); + files.addAll(base_files); + files.add("Test.java"); + + compile(false, files); + + for (String f: extra_files) { + files = new ArrayList(); + files.addAll(base_files); + files.addAll(extra_files); + files.remove(f); + files.add("Test.java"); + compile(false, files); + } + + if (errors > 0) + throw new Exception(errors + " errors occurred"); + } + + void compile(boolean stdBootClassPath, String... files) { + compile(stdBootClassPath, Arrays.asList(files)); + } + + void compile(boolean stdBootClassPath, List files) { + File empty = new File("empty"); + empty.mkdirs(); + + List args = new ArrayList(); + args.add("-classpath"); + args.add("empty"); + + if (!stdBootClassPath) { + args.add("-bootclasspath"); + args.add("empty"); + } + args.add("-d"); + args.add("."); + for (String f: files) + args.add(new File(testSrc, f).getPath()); + + System.out.println("Compile: " + args); + StringWriter out = new StringWriter(); + int rc = com.sun.tools.javac.Main.compile(args.toArray(new String[args.size()]), + new PrintWriter(out)); + System.out.println(out.toString()); + System.out.println("result: " + rc); + System.out.println(); + + if (rc != 0) + errors++; + } + + private int errors; +} + + diff --git a/Irony.Samples/SourceSamples/Java/Sample 2.java b/Irony.Samples/SourceSamples/Java/Sample 2.java new file mode 100644 index 0000000..cdbcc62 --- /dev/null +++ b/Irony.Samples/SourceSamples/Java/Sample 2.java @@ -0,0 +1,87 @@ +/* + * Copyright 2006-2007 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.*; +import javax.annotation.processing.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; +import static javax.tools.Diagnostic.Kind.*; + +import java.util.Set; + +@SupportedAnnotationTypes({"*"}) +public class HelloWorldAP extends AbstractProcessor { + Messager msgr = null; + Filer filer = null; + boolean DONE=false; + + @Override + public void init(ProcessingEnvironment penv) { + processingEnv = penv; + msgr=penv.getMessager(); + filer=penv.getFiler(); + } + + public boolean process(Set tes, RoundEnvironment renv ) { + boolean ret = true; + if(!renv.processingOver() && !DONE) { + msgr.printMessage(NOTE, "running process to create HelloWorld."); + try { + Writer pw = filer.createSourceFile("HelloWorld").openWriter(); + pw.write("public class HelloWorld {\n"); + pw.write(" public static void main (String argv[]) {\n"); + pw.write(" System.out.println(\"Hello apt world.\");\n"); + pw.write(" }\n"); + pw.write("}\n"); + pw.flush(); + pw.close(); + + OutputStream os = filer.createClassFile("HelloWorldAP").openOutputStream(); + // the easiest way to create a class file is to copy another one + InputStream is = getClass().getResourceAsStream("HelloWorldAP.class"); + copy(is, os); + is.close(); + os.flush(); + os.close(); + DONE=true; + } + catch (IOException ioe) { + msgr.printMessage(ERROR, ioe.getMessage()); + ret = false; + } + catch (Exception e) { + msgr.printMessage(ERROR, e.getMessage()); + ret = false; + } + } + return ret; + } + + void copy(InputStream is, OutputStream os) throws IOException { + byte[] buf = new byte[8192]; + int n; + while ((n = is.read(buf, 0, buf.length)) > 0) + os.write(buf, 0, n); + } +} diff --git a/Irony.Samples/SourceSamples/Java/Sample 3.java b/Irony.Samples/SourceSamples/Java/Sample 3.java new file mode 100644 index 0000000..2d35294 --- /dev/null +++ b/Irony.Samples/SourceSamples/Java/Sample 3.java @@ -0,0 +1,63 @@ +/* + * Copyright 1997 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4091327 4079087 + * @summary Verify resolution of qualified outer class 'this' references. + * @author William Maddox (maddox) [shamelessly cribbed from bug report] + * + * @clean QualifiedOuterThis QualifiedOuterThis$Y QualifiedOuterThis$Y$Z + * @compile QualifiedOuterThis.java + * @run main QualifiedOuterThis + */ + +public class QualifiedOuterThis { + static StringBuffer sb = new StringBuffer(); + public String toString() { sb.append('X'); return "X"; } + void test() { + class Y { + public String toString() { sb.append('Y'); return "Y"; } + class Z { + public String toString() { sb.append('Z'); return "Z"; } + void test() { + System.out.println(this.toString()); + System.out.println(Y.this.toString()); + System.out.println(QualifiedOuterThis.this.toString()); + } + } + void test() { + new Z().test(); + } + } + new Y().test(); + } + public static void main(String[] s) throws Exception { + QualifiedOuterThis x = new QualifiedOuterThis(); + x.test(); // Print Z Y X + System.out.println(sb.toString()); + if (!sb.toString().equals("ZYX")) { + throw new Exception("incorrect outer instance method called!"); + } + } +} diff --git a/Irony.Samples/SourceSamples/Java/Sample 4.java b/Irony.Samples/SourceSamples/Java/Sample 4.java new file mode 100644 index 0000000..50692fe --- /dev/null +++ b/Irony.Samples/SourceSamples/Java/Sample 4.java @@ -0,0 +1,64 @@ +/* + * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4856541 4812161 + * @summary varags, auto boxing + * @author gafter + * + * @compile -source 1.5 Varargs1.java + * @run main Varargs1 + */ + +public class Varargs1 { + static int f(String s, int a, short b) { + return 1; + } + static int f(String s, int a, int b) { + return 2; + } + static int f(String s, Integer ... args) { + return 3; + } + static int f(String s, Number ... args) { + return 4; + } + static int f(String s, Object ... args) { + return 5; + } + static int f(int ... args) { + return 6; + } + static void check(int expected, int actual) { + if (actual != expected) throw new AssertionError(actual); + } + public static void main(String[] args) { + check(1, f("x", 12, (short)13)); + check(2, f("x", 12, 13)); + check(3, f("x", 12, 13, 14)); + check(4, f("x", 12, 13.5)); + check(5, f("x", 12, true)); + check(6, f(12, 13)); + } +} diff --git a/Irony.Samples/SourceSamples/Java/Sample 5.java b/Irony.Samples/SourceSamples/Java/Sample 5.java new file mode 100644 index 0000000..de024f3 --- /dev/null +++ b/Irony.Samples/SourceSamples/Java/Sample 5.java @@ -0,0 +1,318 @@ +/* + * Copyright 1998 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 4147520 + * @summary Verify correct implementation of qualified 'this' and 'super'. + * @author maddox + * + * @run compile QualifiedThisAndSuper_3.java + * @run main QualifiedThisAndSuper_3 + */ + +class AS { + String s = "ass"; + private String t = "ast"; + protected String u = "asu"; + String m() { return "asm"; } + private String n() { return "asn"; } + protected String o() { return "aso"; } + + static String xs = "xass"; + static private String xt = "xast"; + static protected String xu = "xasu"; + static String xm() { return "xasm"; } + static private String xn() { return "xasn"; } + static protected String xo() { return "xaso"; } +} + +class BS { + String s = "bss"; + private String t = "bst"; + protected String u = "bsu"; + String m() { return "bsm"; } + private String n() { return "bsn"; } + protected String o() { return "bso"; } +} + +class CS { + String s = "css"; + private String t = "cst"; + protected String u = "csu"; + String m() { return "csm"; } + private String n() { return "csn"; } + protected String o() { return "cso"; } +} + +public class QualifiedThisAndSuper_3 extends AS { + + void check(String expr, String result, String expected) { + if (!result.equals(expected)) { + throw new Error("Evaluated "+ expr + + " : result " + result + ", expected " + expected); + } + } + + + QualifiedThisAndSuper_3() { super(); } + String s = "as"; + private String t = "at"; + protected String u = "au"; + String m() { return "am"; } + private String n() { return "an"; } + protected String o() { return "ao"; } + + static String xs = "xas"; + static private String xt = "xat"; + static protected String xu = "xau"; + static String xm() { return "xam"; } + static private String xn() { return "xan"; } + static protected String xo() { return "xao"; } + + public class B extends BS { + B() { super(); } + String s = "bs"; + private String t = "bt"; + protected String u = "bu"; + String m() { return "bm"; } + private String n() { return "bn"; } + protected String o() { return "bo"; } + public class C extends CS { + C() { super(); } + String s = "cs"; + private String t = "ct"; + protected String u = "cu"; + String m() { return "cm"; } + private String n() { return "cn"; } + protected String o() { return "co"; } + void test() { + + check("QualifiedThisAndSuper_3.super.xm()", QualifiedThisAndSuper_3.super.xm(), "xasm"); + // Private to another package-member class: not accessible + // check("QualifiedThisAndSuper_3.super.xn()", QualifiedThisAndSuper_3.super.xn(), "xasn"); + check("QualifiedThisAndSuper_3.super.xo()", QualifiedThisAndSuper_3.super.xo(), "xaso"); + + check("QualifiedThisAndSuper_3.super.xs", QualifiedThisAndSuper_3.super.xs, "xass"); + // Private to another package-member class: not accessible + // check("QualifiedThisAndSuper_3.super.xt", QualifiedThisAndSuper_3.super.xt, "xast"); + check("QualifiedThisAndSuper_3.super.xu", QualifiedThisAndSuper_3.super.xu, "xasu"); + + check("QualifiedThisAndSuper_3.this.xm()", QualifiedThisAndSuper_3.this.xm(), "xam"); + check("QualifiedThisAndSuper_3.this.xn()", QualifiedThisAndSuper_3.this.xn(), "xan"); + check("QualifiedThisAndSuper_3.this.xo()", QualifiedThisAndSuper_3.this.xo(), "xao"); + + check("QualifiedThisAndSuper_3.this.xs", QualifiedThisAndSuper_3.this.xs, "xas"); + check("QualifiedThisAndSuper_3.this.xt", QualifiedThisAndSuper_3.this.xt, "xat"); + check("QualifiedThisAndSuper_3.this.xu", QualifiedThisAndSuper_3.this.xu, "xau"); + + //--- + + check("this.m()", this.m(), "cm"); + + check("QualifiedThisAndSuper_3.this.m()", QualifiedThisAndSuper_3.this.m(), "am"); + check("B.this.m()", B.this.m(), "bm"); + check("C.this.m()", C.this.m(), "cm"); + + check("super.m()", super.m(), "csm"); + + check("QualifiedThisAndSuper_3.super.m()", QualifiedThisAndSuper_3.super.m(), "asm"); + check("B.super.m()", B.super.m(), "bsm"); + check("C.super.m()", C.super.m(), "csm"); + + // should re-use access methods. + check("QualifiedThisAndSuper_3.super.m()", QualifiedThisAndSuper_3.super.m(), "asm"); + check("B.super.m()", B.super.m(), "bsm"); + check("C.super.m()", C.super.m(), "csm"); + + //--- + + check("this.n()", this.n(), "cn"); + + check("QualifiedThisAndSuper_3.this.n()", QualifiedThisAndSuper_3.this.n(), "an"); + check("B.this.n()", B.this.n(), "bn"); + check("C.this.n()", C.this.n(), "cn"); + + /***** + check("super.n()", super.n(), "csn"); + + check("QualifiedThisAndSuper_3.super.n()", QualifiedThisAndSuper_3.super.n(), "asn"); + check("B.super.n()", B.super.n(), "bsn"); + check("C.super.n()", C.super.n(), "csn"); + + // should re-use access methods. + check("QualifiedThisAndSuper_3.super.n()", QualifiedThisAndSuper_3.super.n(), "asn"); + check("B.super.n()", B.super.n(), "bsn"); + check("C.super.n()", C.super.n(), "csn"); + *****/ + + //--- + + check("this.o()", this.o(), "co"); + + check("QualifiedThisAndSuper_3.this.o()", QualifiedThisAndSuper_3.this.o(), "ao"); + check("B.this.o()", B.this.o(), "bo"); + check("C.this.o()", C.this.o(), "co"); + + check("super.o()", super.o(), "cso"); + + check("QualifiedThisAndSuper_3.super.o()", QualifiedThisAndSuper_3.super.o(), "aso"); + check("B.super.o()", B.super.o(), "bso"); + check("C.super.o()", C.super.o(), "cso"); + + // should re-use access methods. + check("QualifiedThisAndSuper_3.super.o()", QualifiedThisAndSuper_3.super.o(), "aso"); + check("B.super.o()", B.super.o(), "bso"); + check("C.super.o()", C.super.o(), "cso"); + + //--- + + check("this.s", this.s, "cs"); + + check("QualifiedThisAndSuper_3.this.s", QualifiedThisAndSuper_3.this.s, "as"); + check("B.this.s", B.this.s, "bs"); + check("C.this.s", C.this.s, "cs"); + + //--- + + check("this.t", this.t, "ct"); + + check("QualifiedThisAndSuper_3.this.t", QualifiedThisAndSuper_3.this.t, "at"); + check("B.this.t", B.this.t, "bt"); + check("C.this.t", C.this.t, "ct"); + + //--- + + check("this.u", this.u, "cu"); + + check("QualifiedThisAndSuper_3.this.u", QualifiedThisAndSuper_3.this.u, "au"); + check("B.this.u", B.this.u, "bu"); + check("C.this.u", C.this.u, "cu"); + + //--- + + check("super.s", super.s, "css"); + + check("QualifiedThisAndSuper_3.super.s", QualifiedThisAndSuper_3.super.s, "ass"); + check("B.super.s", B.super.s, "bss"); + check("C.super.s", C.super.s, "css"); + + //--- + + /***** + check("super.t", super.t, "cst"); + + check("QualifiedThisAndSuper_3.super.t", QualifiedThisAndSuper_3.super.t, "ast"); + check("B.super.t", B.super.t, "bst"); + check("C.super.t", C.super.t, "cst"); + *****/ + + //--- + + check("super.u", super.u, "csu"); + + check("QualifiedThisAndSuper_3.super.u", QualifiedThisAndSuper_3.super.u, "asu"); + check("B.super.u", B.super.u, "bsu"); + check("C.super.u", C.super.u, "csu"); + + //--- + + QualifiedThisAndSuper_3.this.s = "foo"; + System.out.println(QualifiedThisAndSuper_3.this.s); + check("QualifiedThisAndSuper_3.this.s", QualifiedThisAndSuper_3.this.s, "foo"); + B.this.s = "bar"; + System.out.println(B.this.s); + check("B.this.s", B.this.s, "bar"); + C.this.s = "baz"; + System.out.println(C.this.s); + check("C.this.s", C.this.s, "baz"); + + QualifiedThisAndSuper_3.this.t = "foo"; + System.out.println(QualifiedThisAndSuper_3.this.t); + check("QualifiedThisAndSuper_3.this.t", QualifiedThisAndSuper_3.this.t, "foo"); + B.this.t = "bar"; + System.out.println(B.this.t); + check("B.this.t", B.this.t, "bar"); + C.this.t = "baz"; + System.out.println(C.this.t); + check("C.this.t", C.this.t, "baz"); + + QualifiedThisAndSuper_3.this.u = "foo"; + System.out.println(QualifiedThisAndSuper_3.this.u); + check("QualifiedThisAndSuper_3.this.u", QualifiedThisAndSuper_3.this.u, "foo"); + B.this.u = "bar"; + System.out.println(B.this.u); + check("B.this.u", B.this.u, "bar"); + C.this.u = "baz"; + System.out.println(C.this.u); + check("C.this.u", C.this.u, "baz"); + + QualifiedThisAndSuper_3.super.s = "foo"; + System.out.println(QualifiedThisAndSuper_3.super.s); + check("QualifiedThisAndSuper_3.super.s", QualifiedThisAndSuper_3.super.s, "foo"); + B.super.s = "bar"; + System.out.println(B.super.s); + check("B.super.s", B.super.s, "bar"); + C.super.s = "baz"; + System.out.println(C.super.s); + check("C.super.s", C.super.s, "baz"); + + /***** + QualifiedThisAndSuper_3.super.t = "foo"; + System.out.println(QualifiedThisAndSuper_3.super.t); + check("QualifiedThisAndSuper_3.super.t", QualifiedThisAndSuper_3.super.t, "foo"); + B.super.t = "bar"; + System.out.println(B.super.t); + check("B.super.t", B.super.t, "bar"); + C.super.t = "baz"; + System.out.println(C.super.t); + check("C.super.t", C.super.t, "baz"); + *****/ + + QualifiedThisAndSuper_3.super.u = "foo"; + System.out.println(QualifiedThisAndSuper_3.super.u); + check("QualifiedThisAndSuper_3.super.u", QualifiedThisAndSuper_3.super.u, "foo"); + B.super.u = "bar"; + System.out.println(B.super.u); + check("B.super.u", B.super.u, "bar"); + C.super.u = "baz"; + System.out.println(C.super.u); + check("C.super.u", C.super.u, "baz"); + + } + } + void test() throws Exception { + C c = new C(); + c.test(); + } + } + void test() throws Exception { + B b = new B(); + b.test(); + } + + public static void main(String[] args) throws Exception { + QualifiedThisAndSuper_3 a = new QualifiedThisAndSuper_3(); + a.test(); + } +} diff --git a/Irony.Samples/SourceSamples/Java/Sample 6.java b/Irony.Samples/SourceSamples/Java/Sample 6.java new file mode 100644 index 0000000..3cced2d --- /dev/null +++ b/Irony.Samples/SourceSamples/Java/Sample 6.java @@ -0,0 +1,214 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 5009601 5010455 5005748 + * @summary enum constructors can be declared private + * @author Joseph D. Darcy + * + * @compile -source 1.5 EnumImplicitPrivateConstructor.java + * @run main EnumImplicitPrivateConstructor + */ + +import java.util.*; +import java.lang.reflect.*; +import java.lang.annotation.*; + +/* + * Arguably, only the final and abstract should be held in + * ExpectedModifiers; whether or not an enum should be static could be + * inferred from getDeclaringClass and working versions of + * getEnclosingMethod and getEnclosingConstructor. I.e. if + * getDeclaringClass, getEnclosingMethod, and getEnclosingConstructor + * were all null, the enum is a top-level class and should not be + * static; otherwise, it should be static. + */ + +@ExpectedModifiers(Modifier.FINAL) +public enum EnumImplicitPrivateConstructor { + RED(255, 0, 0), + GREEN(0, 255, 0), + BLUE(0, 0, 255); + + private int r, g, b; + EnumImplicitPrivateConstructor(int r, int g, int b) { + this.r = r; + this.g = g; + this.b = b; + } + + /* + * Using reflection, Verify that + * 1. all non-synthetic constructors of enum classes are marked as private. + * 2. top-level enum classes are marked as static + * 3. enum's are marked final and abstract as appropriate + * 4. enum constructors *cannot* be invoked reflectively + */ + public static void main(String argv[]) throws Exception { + boolean passed = true; + + Collection classes = new LinkedHashSet(); + + classes.add(Class.forName("EnumImplicitPrivateConstructor")); + classes.add(Class.forName("EnumImplicitPrivateConstructor$AnotherEnum")); + classes.add(Class.forName("EnumImplicitPrivateConstructor$YetAnotherEnum")); + classes.add(Class.forName("EnumImplicitPrivateConstructor$OneMoreEnum")); + + // Add classes of specialized enum constants + for(Enum e: YetAnotherEnum.values()) + classes.add(e.getClass()); + + for(Class clazz: classes) { + System.out.println("Testing class " + clazz); + + int classModifiers = clazz.getModifiers(); + + // Why is this cast needed? + ExpectedModifiers em = (ExpectedModifiers)clazz.getAnnotation(ExpectedModifiers.class); + if (em != null) { + System.out.println("\tTesting expected modifiers"); + int expected = em.value(); + + if (expected != (classModifiers & (Modifier.ABSTRACT|Modifier.FINAL|Modifier.STATIC))) { + passed = false; + System.out.println("\tFAILED: Expected 0x" + Integer.toHexString(expected) + + " got 0x" +Integer.toHexString(classModifiers)); + } + } + + for(Constructor ctor: clazz.getDeclaredConstructors() ) { + System.out.println("\tTesting constructor " + ctor); + + // We don't need no stinkin' access rules + try { + ctor.setAccessible(true); + } catch (java.security.AccessControlException ex) { + } + + int modifiers = ctor.getModifiers(); + + /* + * If clazz is for a specialized enum constant, the + * class will have the ENUM bit set but clazz.isEnum() + * will be false. A constructor in such a class must + * be non-private to allow the parent class to call + * the constructor. Therefore, only impose the + * private constructor check for genuine isEnum + * classes. + */ + if (clazz.isEnum()) { + if ((modifiers & Modifier.PRIVATE) == 0 && + ! ctor.isSynthetic() ) { + passed = false; + System.out.println("\tFAILED: Constructor not marked private: modifiers 0x" + + Integer.toHexString(modifiers)); + } + } + + try { + // Should get exception trying to invoke + Object o = null; + try { + o = ctor.newInstance("abc", 123); + } catch (IllegalAccessException ex) { + } + + /* + * A better test would query the number (and type) + * of parameters and create an appropriate + * argument list since IllegalArgumentException can be + * thrown for just using the wrong number of arguments. + */ + + if (o != null) { + passed = false; + System.err.println("Error: Created new enum object!"); + System.err.println(o.getClass()); + System.err.println(o.toString()); + } + } catch (IllegalArgumentException iae) {} + + } + } + + if (!passed) + throw new RuntimeException("Error during testing."); + } + + + /* + * Should be final and not abstract. + */ + @ExpectedModifiers(Modifier.FINAL|Modifier.STATIC) + enum AnotherEnum { + YELLOW, + CYAN, + MAGENTA; + } + + /* + * Should be neither final nor abstract. + */ + @ExpectedModifiers(Modifier.STATIC) + enum YetAnotherEnum { + GREEN { + int value(){ return 1;} + }, + + ORANGE { + int value(){ return 2;} + }, + + VIOLET { + int value(){ return 3;} + }; + + int value(){ return 0;} + } + + /* + * Should be abstract and not final. + */ + @ExpectedModifiers(Modifier.ABSTRACT|Modifier.STATIC) + enum OneMoreEnum { + SANGUINE { + int value(){ return 1;} + }, + + VERDANT { + int value(){ return 2;} + }, + + CERULEAN { + int value(){ return 3;} + }; + + abstract int value(); + } +} + +@Retention(RetentionPolicy.RUNTIME) +@interface ExpectedModifiers { + int value(); +} diff --git a/Irony.Samples/SourceSamples/Java/about_samples.txt b/Irony.Samples/SourceSamples/Java/about_samples.txt new file mode 100644 index 0000000..c9979cf --- /dev/null +++ b/Irony.Samples/SourceSamples/Java/about_samples.txt @@ -0,0 +1 @@ +These samples were provided by Jere Jones, the creator of Java Grammar. \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/MiniPython/PerfTest.py b/Irony.Samples/SourceSamples/MiniPython/PerfTest.py new file mode 100644 index 0000000..9c3fd6a --- /dev/null +++ b/Irony.Samples/SourceSamples/MiniPython/PerfTest.py @@ -0,0 +1,119 @@ +# Used for perf measurements. Defines a simple function and makes 100 calls in a row. +# I ran 10K calls, total 1 million invocations, total time is around 250ms on my laptop (64bit 2.7GHz, Release mode, running .exe directly, no debugger); +# 0.4 microseconds per call. + +m = 5 + +def addm(x, y): + x + y + m + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + +x = addm(1, 2) +x = addm(2, 3) +x = addm(3, 4) +x = addm(4, 5) +x = addm(5, 6) +x = addm(6, 7) +x = addm(7, 8) +x = addm(8, 9) +x = addm(9, 10) +x = addm(10, 11) + diff --git a/Irony.Samples/SourceSamples/MiniPython/TestFunCall.py b/Irony.Samples/SourceSamples/MiniPython/TestFunCall.py new file mode 100644 index 0000000..7b94a51 --- /dev/null +++ b/Irony.Samples/SourceSamples/MiniPython/TestFunCall.py @@ -0,0 +1,5 @@ +def add(x, y): + x + y + +add(3, 4) + \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/SQL/sql_op_test.txt b/Irony.Samples/SourceSamples/SQL/sql_op_test.txt new file mode 100644 index 0000000..3aa55ad --- /dev/null +++ b/Irony.Samples/SourceSamples/SQL/sql_op_test.txt @@ -0,0 +1,4 @@ +-- testing operator precedence with various capitalization of OR,AND operators +SELECT Name +FROM Product +where A oR B and C OR D + X * 5; diff --git a/Irony.Samples/SourceSamples/SQL/sql_sample1.txt b/Irony.Samples/SourceSamples/SQL/sql_sample1.txt new file mode 100644 index 0000000..6b95c11 --- /dev/null +++ b/Irony.Samples/SourceSamples/SQL/sql_sample1.txt @@ -0,0 +1,7 @@ +SELECT Name +FROM Production.Product +WHERE ListPrice >= ANY ( + SELECT MAX (ListPrice) + FROM Production.Product + GROUP BY ProductSubcategoryID +) diff --git a/Irony.Samples/SourceSamples/SQL/sql_sample2.txt b/Irony.Samples/SourceSamples/SQL/sql_sample2.txt new file mode 100644 index 0000000..be79b10 --- /dev/null +++ b/Irony.Samples/SourceSamples/SQL/sql_sample2.txt @@ -0,0 +1,21 @@ +CREATE TABLE [Employees] ( + "EmployeeID" int NOT NULL , + "LastName" nvarchar (20) NOT NULL , + "FirstName" nvarchar (10) NOT NULL , + "Title" nvarchar (30) NULL , + "TitleOfCourtesy" nvarchar (25) NULL , + "BirthDate" datetime NULL , + "HireDate" datetime NULL , + "Address" nvarchar (60) NULL , + "City" nvarchar (15) NULL , + "Region" nvarchar (15) NULL , + "PostalCode" nvarchar (10) NULL , + "Country" nvarchar (15) NULL , + "HomePhone" nvarchar (24) NULL , + "Extension" nvarchar (4) NULL , + "Photo" image NULL , + "Notes" ntext NULL , + "ReportsTo" int NULL , + "PhotoPath" nvarchar (255) NULL +) +GO \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/SQL/sql_sample3.txt b/Irony.Samples/SourceSamples/SQL/sql_sample3.txt new file mode 100644 index 0000000..eee7c22 --- /dev/null +++ b/Irony.Samples/SourceSamples/SQL/sql_sample3.txt @@ -0,0 +1,25 @@ +SELECT Name + FROM Production.Product +WHERE ProductSubcategoryID IN ( + SELECT ProductSubcategoryID + FROM Production.ProductSubcategory + WHERE Name = 'Wheels' +) +go; + +SELECT au_lname, state +FROM authors +WHERE state IN ('CA', 'IN', 'MD'); + +SELECT [au_lname], "au_fname" +FROM authors +WHERE au_id NOT IN + (SELECT au_id + FROM titleauthor + WHERE royaltyper < 50) + + + + + + diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/MemberAccess.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/MemberAccess.txt new file mode 100644 index 0000000..52c85e5 --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/MemberAccess.txt @@ -0,0 +1,3 @@ +# Member access sample +# For more samples see EvaluatorTests.cs file in unit tests project. +"0123".Substring(1) + "abcd".Length + "456"[1] # expected '12345' diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/OpSample.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/OpSample.txt new file mode 100644 index 0000000..f7569d8 --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/OpSample.txt @@ -0,0 +1,5 @@ +#Simple calculation +x = 1 + 2 * 3 # =7 +y = --x # = 6 +z = x * 1.5 # = 9 +z -= y # = 3 diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/PerfTest.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/PerfTest.txt new file mode 100644 index 0000000..accb4d7 --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/PerfTest.txt @@ -0,0 +1,1207 @@ +# Used for perf measurements. Just 1000 simple computations +# I ran a loop of 1000 calls to evaluate this script, total 1 million assignment evaluations. +# On my laptop the total time is around 200 ms, giving smth like 200ns per line. (64bit 2.7GHz, Release mode, running .exe directly, no debugger) +# Not bad .... + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + +#Simple calculation +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z +x = 1 + 2 * 3 +y = --x +z = x * 1.5 +z -= y -1 +m = x + y + z + + + diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/StringSample.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/StringSample.txt new file mode 100644 index 0000000..39949d0 --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/StringSample.txt @@ -0,0 +1,4 @@ +# Sample with strings for Expression Evaluator +x = 4 +y = 7 +" #{x} * #{y} " + "= #{x * y}" \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestFormatAndPrint.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestFormatAndPrint.txt new file mode 100644 index 0000000..6d3248a --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestFormatAndPrint.txt @@ -0,0 +1,3 @@ +#print and format are two built-in functions added to ExpressionEvaluator runtime. +print ("Printing and formatting demo:") +print(format("{0} * {1} = {2}", 3, 4, 3 * 4)) diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestIif.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestIif.txt new file mode 100644 index 0000000..b2bf6aa --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/TestIif.txt @@ -0,0 +1,3 @@ +# Testing special form "IIF" +# You can also use equivalent "?" operator: '1 > 0 ? "true" : 1/0' +iif(1 > 0, "true", 1/0) # notice that if-false argument is not evaluated diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseEnv.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseEnv.txt new file mode 100644 index 0000000..e47d3ba --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseEnv.txt @@ -0,0 +1,2 @@ +# simple operation involving properties and methods of System.Environment class +report = "Machine: #{MachineName}, OS: #{OSVersion}, User: #{UserName}" diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseMath.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseMath.txt new file mode 100644 index 0000000..26a71eb --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/UseMath.txt @@ -0,0 +1,2 @@ +# simple calculation using imported methods of System.Math class. +abs(-1.0) + Log10(100.0) + sqrt(9) + floor(4.5) + sin(PI/2) # = 11 diff --git a/Irony.Samples/SourceSamples/SampleExpressionEvaluator/_more_samples.txt b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/_more_samples.txt new file mode 100644 index 0000000..60d8f87 --- /dev/null +++ b/Irony.Samples/SourceSamples/SampleExpressionEvaluator/_more_samples.txt @@ -0,0 +1,4 @@ +# For more samples of expression evaluator use, including use with custom objects, +# arrays, dictionaries, data rows ... + + " See EvaluatorTests.cs in unit tests project. " diff --git a/Irony.Samples/SourceSamples/Scheme/99 bottles simple.scm b/Irony.Samples/SourceSamples/Scheme/99 bottles simple.scm new file mode 100644 index 0000000..f4313bb --- /dev/null +++ b/Irony.Samples/SourceSamples/Scheme/99 bottles simple.scm @@ -0,0 +1,14 @@ +(define (bottles n) + (display n) (display " bottles of beer")) + +(define (beer n) + (if (> n 0) + (begin + (bottles n) (display " on the wall") (newline) + (bottles n) (display " on the wall") (newline) + (display "Take one down, pass it around") (newline) + (bottles (- n 1)) (display " on the wall") (newline) + (newline) + (beer (- n 1))))) + +(beer 100) \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/Scheme/99 bottles.scm b/Irony.Samples/SourceSamples/Scheme/99 bottles.scm new file mode 100644 index 0000000..22015d6 --- /dev/null +++ b/Irony.Samples/SourceSamples/Scheme/99 bottles.scm @@ -0,0 +1,21 @@ +;;; Tim Goodwin (tim@pipex.net) + +(define bottles + (lambda (n) + (cond ((= n 0) (display "No more bottles")) + ((= n 1) (display "One bottle")) + (else (display n) (display " bottles"))) + (display " of beer"))) + +(define beer + (lambda (n) + (if (> n 0) + (begin + (bottles n) (display " on the wall") (newline) + (bottles n) (newline) + (display "Take one down, pass it around") (newline) + (bottles (- n 1)) (display " on the wall") (newline) + (newline) + (beer (- n 1)))))) + +(beer 99) \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/Scheme/Fib.scm b/Irony.Samples/SourceSamples/Scheme/Fib.scm new file mode 100644 index 0000000..2b0cd0b --- /dev/null +++ b/Irony.Samples/SourceSamples/Scheme/Fib.scm @@ -0,0 +1,6 @@ +(define (fib n) + (if (< n 2) + 1 + (+ (fib (- n 1)) (fib (- n 2))))) + +(display (fib 20)) ; fib(20)= 10946 diff --git a/Irony.Samples/SourceSamples/Scheme/cadr.scm b/Irony.Samples/SourceSamples/Scheme/cadr.scm new file mode 100644 index 0000000..150ca5a --- /dev/null +++ b/Irony.Samples/SourceSamples/Scheme/cadr.scm @@ -0,0 +1,6 @@ +(define (cadr lst) + (car (cdr lst))) + +(define mylist (list 1 "foo" 3)) + +(display "result: " (cadr mylist)) diff --git a/Irony.Samples/SourceSamples/SearchGrammar/Search1.txt b/Irony.Samples/SourceSamples/SearchGrammar/Search1.txt new file mode 100644 index 0000000..b26db20 --- /dev/null +++ b/Irony.Samples/SourceSamples/SearchGrammar/Search1.txt @@ -0,0 +1 @@ +("aluminum" +crank) OR tire \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/Wiki/Sample1_codeplex.txt b/Irony.Samples/SourceSamples/Wiki/Sample1_codeplex.txt new file mode 100644 index 0000000..21ee461 --- /dev/null +++ b/Irony.Samples/SourceSamples/Wiki/Sample1_codeplex.txt @@ -0,0 +1,70 @@ +*bold text* +_italicized text_ ++underlined text+ +~~strikethrough text~~ +NORMAL^^superscript text^^ +NORMAL,,subscript text,, +*bold _bold-italic* italic_ normal + +! Heading 1 +!! Heading 2 +!!! Heading 3 + +//horizontal line +---- + +//Bulletted lists +* bullet 1 +** bullet 1.1 +** bullet 1.2 +*** bullet 1.2.1 +*** bullet 1.2.2 +** bullet 1.3 +* bullet 2 +* bullet 3 + +//Numbered lists +# item 1 +## item 1.1 +### item 1.1.1 +### item 1.1.2 +# item 2 +# item 3 + +{" not *formatted* _text_ "} + +<{this is left aligned content}< +>{this is right aligned content}> +: this is indented +:: this is double-indented + +//create an anchor +{anchor:anchorName} + +// link to an anchor +[#anchorName] + +//Link +[url:Link to Google|http://www.google.com] +[url:http://www.google.com] +[url:Email Someone|mailto:someone@someurl.com] + +//Link to file +[file:http://wikiplex.codeplex.com/SourceControl/ListDownloadableCommits.aspx#DownloadLatest] +[file:Download Latest Source|http://wikiplex.codeplex.com/SourceControl/ListDownloadableCommits.aspx#DownloadLatest] + +//Image +[image:Google Logo|http://www.google.com/intl/en_ALL/images/logo.gif] + +//Image with link: +[image:Google Logo|http://www.google.com/intl/en_ALL/images/logo.gif|http://www.google.com] + +|| Table Heading 1 || Table Heading 2 || Table Heading 3 || +| Row 1 - Cell 1 | Row 1 - Cell 2 | Row 1 - Cell 3 | +| Row 2 - Cell 1 | Row 2 - Cell 2 | Row 2 - Cell 3 | + +//code sample (no coloring) +{{ + public void function() { + } +}} diff --git a/Irony.Samples/SourceSamples/Wiki/Sample1_creole.txt b/Irony.Samples/SourceSamples/Wiki/Sample1_creole.txt new file mode 100644 index 0000000..cd4b603 --- /dev/null +++ b/Irony.Samples/SourceSamples/Wiki/Sample1_creole.txt @@ -0,0 +1,54 @@ +normal +**bold text ** +**bold //bold-italic** italic// normal + + += Heading 1 +== Heading 2= +=== Heading 3 **not** //parsed//, ending '=' symbols are ignored === + +horizontal line +---- + +Bulletted lists +* bullet 1 +** bullet 1.1 +** bullet 1.2 +*** bullet 1.2.1 +*** bullet 1.2.2 +** bullet 1.3 +* bullet 2 +* bullet 3 + +Numbered lists +# item 1 +## item 1.1 +### item 1.1.1 +### item 1.1.2 +# item 2 +# item 3 + +{{{ not **formatted** //text// }}} + +Forced line break: +This is the first line,\\and this is the second. + +Link +[[http://www.google.com]] + +Raw link (does not work yet): +http://www.rawlink.org/, http://www.another.rawlink.org + +Image +{{Google Logo|http://www.google.com/intl/en_ALL/images/logo.gif}} + +Linked Image (not implemented): +[[http://www.google.com|{{http://www.google.com/intl/en_ALL/images/logo.gif}}]] + +Table +|= Table Heading 1 |= Table Heading 2 |= Table Heading 3 | +| Row 1 - Cell 1 | Row 1 - Cell 2 | Row 1 - Cell 3 | +| Row 2 - Cell 1 | Row 2 - Cell 2 | Row 2 - Cell 3 | + +Text with escaped tags: ~**abc ~/~/def ~\\ ~~ (nothing should be bold or italic on this line) +~# Not list item, just escaped ~# symbol at the beginning of the line \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/_about.txt b/Irony.Samples/SourceSamples/_about.txt new file mode 100644 index 0000000..e5682f3 --- /dev/null +++ b/Irony.Samples/SourceSamples/_about.txt @@ -0,0 +1,4 @@ +Source code samples in this folder (except expression samples) are taken from the following web site: +http://99-bottles-of-beer.net/ + + diff --git a/Irony.Samples/SourceSamples/c#/TestLessThanOp.cs b/Irony.Samples/SourceSamples/c#/TestLessThanOp.cs new file mode 100644 index 0000000..6670345 --- /dev/null +++ b/Irony.Samples/SourceSamples/c#/TestLessThanOp.cs @@ -0,0 +1,11 @@ +//Simple test for "<" operator, to check it is properly distinguished from "<" in type argument. +namespace Test { + public class TestClass { + public void DoStuff(int arg) { + List x; + List> genericVar; + if (myGenType.Prop < arg) + arg = 5; + } + }//class +}//namespace \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/c#/bottles-v11-long.cs b/Irony.Samples/SourceSamples/c#/bottles-v11-long.cs new file mode 100644 index 0000000..a9e2c7c --- /dev/null +++ b/Irony.Samples/SourceSamples/c#/bottles-v11-long.cs @@ -0,0 +1,147 @@ +/// Implementation of Ninety-Nine Bottles of Beer Song in C#. +/// What's neat is that .NET makes the Binge class a +/// full-fledged component that may be called from any other +/// .NET component. +/// +/// Paul M. Parks +/// http://www.parkscomputing.com/ +/// February 8, 2002 +/// + +using System; + +namespace NinetyNineBottles +{ + /// + /// References the method of output. + /// + public delegate void Writer(string format, params object[] arg); + + /// + /// References the corrective action to take when we run out. + /// + public delegate int MakeRun(); + + /// + /// The act of consuming all those beverages. + /// + public class Binge + { + /// + /// What we'll be drinking. + /// + private string beverage; + + /// + /// The starting count. + /// + private int count = 0; + + /// + /// The manner in which the lyrics are output. + /// + private Writer Sing; + + /// + /// What to do when it's all gone. + /// + private MakeRun RiskDUI; + + public event MakeRun OutOfBottles; + + + /// + /// Initializes the binge. + /// + /// How many we're consuming. + /// + /// Our instructions, should we succeed. + /// + /// How our drinking song will be heard. + /// What to drink during this binge. + public Binge(string beverage, int count, Writer writer) + { + this.beverage = beverage; + this.count = count; + this.Sing = writer; + } + + /// + /// Let's get started. + /// + public void Start() + { + while (count > 0) + { + Sing( + @" +{0} bottle{1} of {2} on the wall, +{0} bottle{1} of {2}. +Take one down, pass it around,", + count, (count == 1) ? "" : "s", beverage); + + count--; + + if (count > 0) + { + Sing("{0} bottle{1} of {2} on the wall.", + count, (count == 1) ? "" : "s", beverage); + } + else + { + Sing("No more bottles of {0} on the wall.", beverage, null); + } + + } + + Sing( + @" +No more bottles of {0} on the wall, +No more bottles of {0}.", beverage, null); + + if (this.OutOfBottles != null) + { + count = this.OutOfBottles(); + Sing("{0} bottles of {1} on the wall.", count, beverage); + } + else + { + Sing("First we weep, then we sleep."); + Sing("No more bottles of {0} on the wall.", beverage, null); + } + } + } + + /// + /// The song remains the same. + /// + class SingTheSong + { + /// + /// Any other number would be strange. + /// + const int bottleCount = 99; + + /// + /// The entry point. Sets the parameters of the Binge and starts it. + /// + /// unused + static void Main(string[] args) + { + Binge binge = + new Binge("beer", bottleCount, new Writer(Console.WriteLine)); + binge.OutOfBottles += new MakeRun(SevenEleven); + binge.Start(); + } + + /// + /// There's bound to be one nearby. + /// + /// Whatever would fit in the trunk. + static int SevenEleven() + { + Console.WriteLine("Go to the store, get some more..."); + return bottleCount; + } + } +} \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/c#/bottles-v11-short.cs b/Irony.Samples/SourceSamples/c#/bottles-v11-short.cs new file mode 100644 index 0000000..d703452 --- /dev/null +++ b/Irony.Samples/SourceSamples/c#/bottles-v11-short.cs @@ -0,0 +1,68 @@ +// Simplistic, yet working C# sample +// Author: Mark Hurley (markph@mailcan.com) +// May 30, 2005 + +using System; + +namespace NinetyNineBottlesOfBeer +{ + /// + /// Infamous 99 bottles of beer song in C#.Net + /// + class NinetyNineBottlesOfBeerSong + { + /// + /// beer verse more beer left + /// + private const string BEER_LYRICS_MORE = @" +{0} bottle{1} of beer on the wall, +{0} bottle{1} of beer. +Take one down, pass it around, +{2} bottle{3} of beer on the wall."; + + /// + /// beer verse no more beer left + /// + private const string BEER_LYRICS_NONE = @" +{0} bottle{1} of beer on the wall, +{0} bottle{1} of beer. +Take one down, pass it around, +No more bottles of beer on the wall."; + + /// + /// Determine the proper verse, then merge it with count. + /// + /// Number of bottles remaining. + /// Properly formated string verse for song. + public string Sing(int count) + { + string tmp = ""; + if (count == 1) + return string.Format(BEER_LYRICS_NONE, + count, + (count==1) ? "" : "s"); + else if (count > 0) + return string.Format(BEER_LYRICS_MORE, + count, + (count==1) ? "" : "s", + (count-1), + ((count-1)==1) ? "" : "s"); + else + tmp = ""; + + return tmp; + } + + [STAThread] + static void Main(string[] args) + { + NinetyNineBottlesOfBeerSong song = new NinetyNineBottlesOfBeerSong(); + + for(int i=99; i>0; i--) + { + Console.WriteLine(song.Sing(i)); + Console.ReadLine(); + } + } + } +} diff --git a/Irony.Samples/SourceSamples/c#/bottles-v20.cs b/Irony.Samples/SourceSamples/c#/bottles-v20.cs new file mode 100644 index 0000000..ff1ce8d --- /dev/null +++ b/Irony.Samples/SourceSamples/c#/bottles-v20.cs @@ -0,0 +1,79 @@ +// C# 2.0 Ninety Nine Bottles of Beer Example +// Bradley Tetzlaff +// 2005-11-23 + +using System; + +namespace NinetyNineBottlesOfBeer +{ + /// + /// An example of the 99 Bottles of Beer song in C# 2.0, + /// with intentional use of new features of version 2.0. + /// + [CLSCompliant(true)] + public static class NinetyNineBottlesOfBeerSong + { + /// Singing method type. + /// The bottle index. + /// The song verse. + private delegate string SingVerseMethod(int bi); + + /// The pluralization method type. + /// The bottle index. + /// + /// An "bottles" if plural, otherwise "bottle". + /// + private delegate string Pluralizer(int bi); + + /// + /// Prints the 99 Bottles of Beer song to the console. + /// + public static void Main() + { + // Lyric Parts + const string LYRICS_SOME = @" +{0} of beer on the wall, +{0} of beer. +Take one down, pass it around, +{1}"; + const string LYRICS_NEXT = " of beer on the wall."; + const string LYRICS_NONE = "No more bottles of beer on the wall."; + + const string LYRICS_ZERO = @" +No more bottles of beer on the wall, +no more bottles of beer. +Go to the store and buy some more, +99 bottles of beer on the wall."; + + // Anonymous Methods + // Returns the correct pluralization of "bottle". + Pluralizer bottles = delegate(int bi) + { return (bi == 1) ? "1 bottle" : bi + " bottles"; }; + + // Sings one verse of the 99 Bottles of Beer song. + SingVerseMethod sing = delegate(int bi) + { + if (bi > 0) // More than one beer. + { + return string.Format(LYRICS_SOME, bottles(bi), + bottles(bi - 1) + LYRICS_NEXT); + } + else if (bi == 1) // One beer. + { + return string.Format(LYRICS_SOME, bottles(bi), + LYRICS_NONE); + } + else if (bi == 0) // No beers. + return LYRICS_ZERO; + else + throw new IndexOutOfRangeException("Invalid beer amount."); + }; + // Sing all 99. + for (int bi = 99; bi >= 0; bi--) + { + Console.WriteLine(sing(bi)); + Console.ReadLine(); + } + } + } +} diff --git a/Irony.Samples/SourceSamples/c#/bottles-v30.cs b/Irony.Samples/SourceSamples/c#/bottles-v30.cs new file mode 100644 index 0000000..b2dff0d --- /dev/null +++ b/Irony.Samples/SourceSamples/c#/bottles-v30.cs @@ -0,0 +1,44 @@ +/// A short and sweet C# 3.5 / LINQ implementation of 99 Bottles of Beer +/// Jeff Dietrich, jd@discordant.org - October 26, 2007 + +//NOTE by rivantsov: this sample does not work in Irony +// Irony's c# parser cannot parse this file because LINQ queries are not supported yet. + +using System; +using System.Linq; +using System.Text; + +namespace NinetyNineBottles +{ + class Beer + { + static void Main(string[] args) + { + StringBuilder beerLyric = new StringBuilder(); + string nl = System.Environment.NewLine; + + var beers = + (from n in Enumerable.Range(0, 100) + select new { + Say = n == 0 ? "No more bottles" : + (n == 1 ? "1 bottle" : n.ToString() + " bottles"), + Next = n == 1 ? "no more bottles" : + (n == 0 ? "99 bottles" : + (n == 2 ? "1 bottle" : n.ToString() + " bottles")), + Action = n == 0 ? "Go to the store and buy some more" : + "Take one down and pass it around" + }).Reverse(); + + foreach (var beer in beers) + { + beerLyric.AppendFormat("{0} of beer on the wall, {1} of beer.{2}", + beer.Say, beer.Say.ToLower(), nl); + beerLyric.AppendFormat("{0}, {1} of beer on the wall.{2}", + beer.Action, beer.Next, nl); + beerLyric.AppendLine(); + } + Console.WriteLine(beerLyric.ToString()); + Console.ReadLine(); + } + } +} diff --git a/Irony.Samples/SourceSamples/c#/escapes_in_IDs.cs b/Irony.Samples/SourceSamples/c#/escapes_in_IDs.cs new file mode 100644 index 0000000..3979ee9 --- /dev/null +++ b/Irony.Samples/SourceSamples/c#/escapes_in_IDs.cs @@ -0,0 +1,25 @@ +//example from c# spec, page 93 +namespace Sample { + class @class + { + public static void @static(bool @bool) { + if (@bool) + System.Console.WriteLine("true"); + else + System.Console.WriteLine("false"); + } + } + class Class1 + { + static void M() { + cl\u0061ss.st\u0061tic(true); + } + } + +} + +/* code comment from spec: +[the code] defines a class named “class” with a static method named “static” that takes a parameter named +“bool”. Note that since Unicode escapes are not permitted in keywords, the token “cl\u0061ss” is an +identifier, and is the same identifier as “@class”. end example] +*/ \ No newline at end of file diff --git a/Irony.Samples/SourceSamples/fib.py b/Irony.Samples/SourceSamples/fib.py new file mode 100644 index 0000000..8d3c4b2 --- /dev/null +++ b/Irony.Samples/SourceSamples/fib.py @@ -0,0 +1,7 @@ +def fib (n): + if n < 2: + return 1 + else: + return fib (n - 1) + fib(n - 2) + +fib(34) diff --git a/Irony.Samples/TestGrammars/GrammarEx434.cs b/Irony.Samples/TestGrammars/GrammarEx434.cs new file mode 100644 index 0000000..0ea753a --- /dev/null +++ b/Irony.Samples/TestGrammars/GrammarEx434.cs @@ -0,0 +1,84 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + + +namespace Irony.Samples { + //Expression grammar (4.19), from example 4.34 in Dragon book, p.222 + class GrammarEx434 : Grammar { + public GrammarEx434() { + NonTerminal E = new NonTerminal("E"); + NonTerminal T = new NonTerminal("T"); + NonTerminal F = new NonTerminal("F"); + Terminal id = new Terminal("id"); + + E.Rule = E + "+" + T | T; + T.Rule = T + "*" + F | F; + F.Rule = "(" + E + ")" | id; + this.Root = E; + } +/* LR(0) item list for above grammar (identical to fig. 4.35 in dragon book, p.225) +State I0 + E' -> ·E + E -> ·E + T + E -> ·T + T -> ·T * F + T -> ·F + F -> ·( E ) + F -> ·id +State I1 + E' -> E · + E -> E ·+ T +State I2 + E -> T · + T -> T ·* F +State I3 + T -> F · +State I4 + F -> ( ·E ) + E -> ·E + T + E -> ·T + T -> ·T * F + T -> ·F + F -> ·( E ) + F -> ·id +State I5 + F -> id · +State I6 + E -> E + ·T + T -> ·T * F + T -> ·F + F -> ·( E ) + F -> ·id +State I7 + T -> T * ·F + F -> ·( E ) + F -> ·id +State I8 + E -> E ·+ T + F -> ( E ·) +State I9 + E -> E + T · + T -> T ·* F +State I10 + T -> T * F · +State I11 + F -> ( E ) · + + */ + + } +}//namespace diff --git a/Irony.Samples/TestGrammars/GrammarEx446.cs b/Irony.Samples/TestGrammars/GrammarEx446.cs new file mode 100644 index 0000000..d9c3206 --- /dev/null +++ b/Irony.Samples/TestGrammars/GrammarEx446.cs @@ -0,0 +1,75 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples { + //Sample grammar for lookaheads calculation + // The grammar is from example 4.46 in Dragon book, p 241 + // LALR(1) items set for this grammar is provided in fig. 4.45 on page 245. + // Note that the table in the book contains kernel-only items for each state, + // while we print out all items + class GrammarEx446 : Grammar { + public GrammarEx446() { + // A' is augmented root + NonTerminal S = new NonTerminal("S"); + NonTerminal L = new NonTerminal("L"); + NonTerminal R = new NonTerminal("R"); + Terminal id = new IdentifierTerminal("id"); + + S.Rule = L + "=" + R | R; + L.Rule = "*" + R | id; + R.Rule = L; + this.Root = S; + }//method +//Expected state set: + /* +State I0 + [S' -> ·S , ] + [S -> ·L = R , ] + [S -> ·R , ] + [L -> ·* R , = ] + [L -> ·id , = ] + [R -> ·L , ] +State I1 + [S' -> S · , ] +State I2 + [S -> L ·= R , ] + [R -> L · , ] +State I3 + [S -> R · , ] +State I4 + [L -> * ·R , =/ ] + [R -> ·L , =/ ] + [L -> ·* R , =/ ] + [L -> ·id , =/ ] +State I5 + [L -> id · , =/ ] +State I6 + [S -> L = ·R , ] + [R -> ·L , ] + [L -> ·* R , ] + [L -> ·id , ] +State I7 + [L -> * R · , =/ ] +State I8 + [R -> L · , =/ ] +State I9 + [S -> L = R · , ] + + + */ + } +}//namespace diff --git a/Irony.Samples/TestGrammars/GrammarExL514.cs b/Irony.Samples/TestGrammars/GrammarExL514.cs new file mode 100644 index 0000000..c18ff7f --- /dev/null +++ b/Irony.Samples/TestGrammars/GrammarExL514.cs @@ -0,0 +1,57 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples { + //Sample grammar for lookaheads calculation + // The grammar is from example 5.14 in Kenneth Louden book "Compiler Construction", p 218 + // Grammar: + // A -> (A) | a + // LALR(1) item list for this grammar is provided in example 5.17 on page 225. + class GrammarExL514 : Grammar { + public GrammarExL514() { + NonTerminal A = new NonTerminal("A"); + Terminal a = new Terminal("a"); + + A.Rule = "(" + A + ")" | a; + this.Root = A; + }//method + + }//class + + //Expected state set: + + /* + State I0 + [A' -> ·A , ] + [A -> ·( A ) , ] + [A -> ·a , ] + State I1 + [A' -> A · , ] + State I2 + [A -> ( ·A ) , /) ] + [A -> ·( A ) , ) ] + [A -> ·a , ) ] + State I3 + [A -> a · , /) ] + State I4 + [A -> ( A ·) , /) ] + State I5 + [A -> ( A ) · , /) ] + + */ + +}//namespace diff --git a/Irony.Samples/Wiki/WikiCodeplexGrammar.cs b/Irony.Samples/Wiki/WikiCodeplexGrammar.cs new file mode 100644 index 0000000..3456a11 --- /dev/null +++ b/Irony.Samples/Wiki/WikiCodeplexGrammar.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Irony.Parsing; + +namespace Irony.Samples { + + [Language("Wiki-Codeplex", "1.0", "Wiki/Codeplex markup grammar.")] + public class WikiCodeplexGrammar : Grammar, ICanRunSample { + public WikiCodeplexGrammar() { + this.GrammarComments = +@"A grammar for reading codeplex wiki files and transforming them into HTML +http://codeplex.codeplex.com/wikipage?title=Wiki%20Formatting +http://wikiplex.codeplex.com +"; + //Terminals + var text = new WikiTextTerminal("text"); + //Quote-like terminals + var bold = new WikiTagTerminal("bold", WikiTermType.Format, "*", "b"); + var italic = new WikiTagTerminal("italic",WikiTermType.Format, "_", "i"); + var underline = new WikiTagTerminal("underline", WikiTermType.Format, "+", "u"); + var strike = new WikiTagTerminal("strike", WikiTermType.Format, "~~", "del"); + var super = new WikiTagTerminal("super", WikiTermType.Format, "^^", "sup"); + var sub = new WikiTagTerminal("sub", WikiTermType.Format, ",,", "sub"); + + //Headings + var h1 = new WikiTagTerminal("h1", WikiTermType.Heading, "!", "h1"); + var h2 = new WikiTagTerminal("h2", WikiTermType.Heading, "!!", "h2"); + var h3 = new WikiTagTerminal("h3", WikiTermType.Heading, "!!!", "h3"); + var h4 = new WikiTagTerminal("h4", WikiTermType.Heading, "!!!!", "h4"); + var h5 = new WikiTagTerminal("h5", WikiTermType.Heading, "!!!!!", "h5"); + var h6 = new WikiTagTerminal("h6", WikiTermType.Heading, "!!!!!!", "h6"); + + //Ruler + var ruler = new WikiTagTerminal("ruler", WikiTermType.Heading, "----", "hr"); + + //Bulletted lists + var bl1 = new WikiTagTerminal("bl1", WikiTermType.List, "*", "li") { ContainerHtmlElementName = "ul" }; + var bl2 = new WikiTagTerminal("bl2", WikiTermType.List, "**", "li") { ContainerHtmlElementName = "ul" }; + var bl3 = new WikiTagTerminal("bl3", WikiTermType.List, "***", "li"){ ContainerHtmlElementName = "ul" }; + + //Numbered lists + var nl1 = new WikiTagTerminal("nl1", WikiTermType.List, "#", "li"){ ContainerHtmlElementName = "ol" }; + var nl2 = new WikiTagTerminal("nl2", WikiTermType.List, "##", "li"){ ContainerHtmlElementName = "ol" }; + var nl3 = new WikiTagTerminal("nl3", WikiTermType.List, "###", "li"){ ContainerHtmlElementName = "ol" }; + + //Block tags + var codeBlock = new WikiBlockTerminal("codeBlock", WikiBlockType.CodeBlock, "{{", "}}", "pre"); + var escapedBlock = new WikiBlockTerminal("escapedBlock", WikiBlockType.EscapedText, "{\"", "\"}", "pre"); + var anchor = new WikiBlockTerminal("anchor", WikiBlockType.Anchor, "{anchor:", "}", string.Empty); + + //Links + var linkToAnchor = new WikiBlockTerminal("linkToAnchor", WikiBlockType.LinkToAnchor, "[#", "]", string.Empty); + var url = new WikiBlockTerminal("url", WikiBlockType.Url, "[url:", "]", string.Empty); + var fileLink = new WikiBlockTerminal("fileLink", WikiBlockType.FileLink, "[file:", "]", string.Empty); + var image = new WikiBlockTerminal("image", WikiBlockType.Image, "[image:", "]", string.Empty); + + //Tables + var tableHeading = new WikiTagTerminal("tableHeading", WikiTermType.Table, "||", "th"); + var tableRow = new WikiTagTerminal("tableRow", WikiTermType.Table, "|", "td"); + + //Alignment, indents + var leftAlignStart = new WikiTagTerminal("leftAlignStart", WikiTermType.Format, "<{", string.Empty) + { OpenHtmlTag = "
"}; + var leftAlignEnd = new WikiTagTerminal("leftAlignEnd", WikiTermType.Format, "}<", string.Empty) + { OpenHtmlTag = "
"}; + var rightAlignStart = new WikiTagTerminal("rightAlignStart", WikiTermType.Format, ">{", string.Empty) + { OpenHtmlTag = "
"}; + var rightAlignEnd = new WikiTagTerminal("rightAlignEnd", WikiTermType.Format, "}>", string.Empty) + { OpenHtmlTag = "
"}; + var indentOne = new WikiTagTerminal("indentOne", WikiTermType.Heading, ":", string.Empty) + { OpenHtmlTag = "
", CloseHtmlTag = "
" }; + var indentTwo = new WikiTagTerminal("indentTwo", WikiTermType.Heading, "::", string.Empty) + { OpenHtmlTag = "
", CloseHtmlTag = "
" }; + + //Non-terminals + var wikiElement = new NonTerminal("wikiElement"); + var wikiText = new NonTerminal("wikiText"); + + //Rules + wikiElement.Rule = text | bold | italic | strike | underline | super | sub + | h1 | h2 | h3 | h4 | h5 | h6 | ruler + | bl1 | bl2 | bl3 + | nl1 | nl2 | nl3 + | codeBlock | escapedBlock | anchor + | linkToAnchor | url | fileLink | image + | tableHeading | tableRow + | leftAlignStart | leftAlignEnd | rightAlignStart | rightAlignEnd | indentOne | indentTwo + | NewLine; + wikiText.Rule = MakeStarRule(wikiText, wikiElement); + + this.Root = wikiText; + MarkTransient(wikiElement); + //We need to clear punctuation flag on NewLine, so it is not removed from parse tree + NewLine.SetFlag(TermFlags.IsPunctuation, false); + this.LanguageFlags |= LanguageFlags.DisableScannerParserLink | LanguageFlags.NewLineBeforeEOF; + + } + + public override void SkipWhitespace(ISourceStream source) { + return; //no whitespaces at all + } + + public string RunSample(RunSampleArgs args) { + var converter = new WikiHtmlConverter(); + var html = converter.Convert(this, args.ParsedSample.Tokens); + var path = Path.Combine(Path.GetTempPath(), "@wikiSample.html"); + File.WriteAllText(path, html); + System.Diagnostics.Process.Start(path); + return html; + } + + }//class + + +}//namespace diff --git a/Irony.Samples/Wiki/WikiCreoleGrammar.cs b/Irony.Samples/Wiki/WikiCreoleGrammar.cs new file mode 100644 index 0000000..eb04e39 --- /dev/null +++ b/Irony.Samples/Wiki/WikiCreoleGrammar.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Irony.Parsing; + +namespace Irony.Samples { + //What does not work yet: + // * Raw links, CamelCase words should be interpreted as links + // * Automatic paragraphs + // * Linked images + [Language("Wiki-Creole", "1.0", "Wiki/Creole markup grammar.")] + public class WikiCreoleGrammar : Grammar, ICanRunSample { + + public WikiCreoleGrammar() { + this.GrammarComments = +@"A grammar for parsing Creole wiki files and transforming them into HTML +http://www.wikicreole.org/"; + //Terminals + var text = new WikiTextTerminal("text") { EscapeChar = '~' }; + var lineBreak = new WikiTagTerminal("lineBreak", WikiTermType.Element, @"\\", string.Empty) { OpenHtmlTag = "
\n" }; + //Quote-like terminals + var bold = new WikiTagTerminal("bold", WikiTermType.Format, "**", "strong"); + var italic = new WikiTagTerminal("italic",WikiTermType.Format, "//", "em"); + + //Headings + var h1 = new WikiBlockTerminal("h1", WikiBlockType.EscapedText, "=", "\n", "h1"); + var h2 = new WikiBlockTerminal("h2", WikiBlockType.EscapedText, "==", "\n", "h2"); + var h3 = new WikiBlockTerminal("h3", WikiBlockType.EscapedText, "===", "\n", "h3"); + var h4 = new WikiBlockTerminal("h4", WikiBlockType.EscapedText, "====", "\n", "h4"); + var h5 = new WikiBlockTerminal("h5", WikiBlockType.EscapedText, "=====", "\n", "h5"); + var h6 = new WikiBlockTerminal("h6", WikiBlockType.EscapedText, "======", "\n", "h6"); + + //Bulletted lists + var bl1 = new WikiTagTerminal("bl1", WikiTermType.List, "*", "li") { ContainerHtmlElementName = "ul" }; + var bl2 = new WikiTagTerminal("bl2", WikiTermType.List, "**", "li") { ContainerHtmlElementName = "ul" }; + var bl3 = new WikiTagTerminal("bl3", WikiTermType.List, "***", "li"){ ContainerHtmlElementName = "ul" }; + + //Numbered lists + var nl1 = new WikiTagTerminal("nl1", WikiTermType.List, "#", "li"){ ContainerHtmlElementName = "ol" }; + var nl2 = new WikiTagTerminal("nl2", WikiTermType.List, "##", "li"){ ContainerHtmlElementName = "ol" }; + var nl3 = new WikiTagTerminal("nl3", WikiTermType.List, "###", "li"){ ContainerHtmlElementName = "ol" }; + + //Ruler + var ruler = new WikiTagTerminal("ruler", WikiTermType.Heading, "----", "hr"); + + //Image + var image = new WikiBlockTerminal("image", WikiBlockType.Image, "{{", "}}", string.Empty); + + //Link + var link = new WikiBlockTerminal("link", WikiBlockType.Url, "[[", "]]", string.Empty); + + //Tables + var tableHeading = new WikiTagTerminal("tableHeading", WikiTermType.Table, "|=", "th"); + var tableRow = new WikiTagTerminal("tableRow", WikiTermType.Table, "|", "td"); + + //Block tags + //TODO: implement full rules for one-line and multi-line nowiki element + var nowiki = new WikiBlockTerminal("nowiki", WikiBlockType.EscapedText, "{{{", "}}}", "pre"); + + //Paragraph - not used in rules but added by postprocessing + //_paragraph = new WikiTagTerminal("para", WikiTermType. + //Non-terminals + var wikiElement = new NonTerminal("wikiElement"); + var wikiText = new NonTerminal("wikiText"); + + //Rules + wikiElement.Rule = text | lineBreak | bold | italic + | h1 | h2 | h3 | h4 | h5 | h6 + | bl1 | bl2 | bl3 + | nl1 | nl2 | nl3 + | ruler | image | nowiki | link + | tableHeading | tableRow + | NewLine; + wikiText.Rule = MakeStarRule(wikiText, wikiElement); + + this.Root = wikiText; + MarkTransient(wikiElement); + //We need to clear punctuation flag on NewLine, so it is not removed from parse tree + NewLine.SetFlag(TermFlags.IsPunctuation, false); + this.LanguageFlags |= LanguageFlags.DisableScannerParserLink | LanguageFlags.NewLineBeforeEOF; + + } + + public override void SkipWhitespace(ISourceStream source) { + return; //no whitespaces at all + } + + + public string RunSample(RunSampleArgs args) { + var converter = new WikiHtmlConverter(); + PreprocessTokens(args.ParsedSample.Tokens); + var html = converter.Convert(this, args.ParsedSample.Tokens); + var path = Path.Combine(Path.GetTempPath(), "@wikiSample.html"); + File.WriteAllText(path, html); + System.Diagnostics.Process.Start(path); + return html; + } + + private void PreprocessTokens(TokenList tokens) { + var result = new TokenList(); + Token prevToken = null; + bool prevIsParaBreak = true; + bool insidePara = true; + foreach(var token in tokens) { + //fix heading ends: ending '=' chars in Headings should be ignored + var wikiTerm = token.Terminal as WikiTerminalBase; + bool isHeading = (wikiTerm != null && wikiTerm.TermType == WikiTermType.Block && wikiTerm.OpenTag.StartsWith("=")); + if (isHeading) { + token.Value = token.ValueString.TrimEnd(' '); //first trim trailing spaces + token.Value = token.ValueString.TrimEnd('='); //now trim ='s + }//if + var termName = token.Terminal.Name; + var paraBreak = termName.StartsWith("h") || termName.StartsWith("bl") || termName.StartsWith("nl") + || termName.StartsWith("table"); + //token.Terminal.Options = TermOptions.IsDelimiter; + + //check for paragraph start + if (!insidePara && !paraBreak && prevIsParaBreak) + + prevToken = token; + prevIsParaBreak = paraBreak; + }//for each + foreach(var token in tokens) { + + } + + }//method + + }//class + +}//namespace diff --git a/Irony.Samples/Wiki/WikiHtmlConverter.cs b/Irony.Samples/Wiki/WikiHtmlConverter.cs new file mode 100644 index 0000000..596b11e --- /dev/null +++ b/Irony.Samples/Wiki/WikiHtmlConverter.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Samples { + + public class WikiHtmlConverter { + private enum TableStatus { + None, + Table, + Cell + } + internal class FlagTable : Dictionary{} + internal class WikiTermStack : Stack { } + + StringBuilder _output; + FlagTable _flags = new FlagTable(); + WikiTermStack _openLists = new WikiTermStack(); + bool _atLineStart = true; + WikiTerminalBase _currentHeader = null; + //Table flags + bool _insideTable = false; + bool _insideCell = false; + WikiTagTerminal _lastTableTag; + + private int CurrentListLevel { + get { return _openLists.Count == 0 ? 0 : _openLists.Peek().OpenTag.Length; } + } + + + //HtmlEncode method - we don't use System.Web.HttpUtility.HtmlEncode method, because System.Web assembly is not part of + // .NET Client profile; so we just embed implementation here + // This is reformatted version of Rick Strahl's original code: http://www.west-wind.com/Weblog/posts/617930.aspx + public static string HtmlEncode(string text) { + if (text == null) return null; + StringBuilder sb = new StringBuilder(text.Length); + int len = text.Length; + for (int i = 0; i < len; i++) + { + switch (text[i]) { + case '<': sb.Append("<"); break; + case '>': sb.Append(">"); break; + case '"': sb.Append("""); break; + case '&': sb.Append("&"); break; + default: + if (text[i] > 159) { + // decimal numeric entity + sb.Append("&#"); + sb.Append(((int)text[i]).ToString()); + sb.Append(";"); + } else + sb.Append(text[i]); + break; + } + } + return sb.ToString(); + } + + public string Convert(Grammar grammar, TokenList tokens) { + _output = new StringBuilder(8192); //8k + _output.AppendLine(""); + + foreach(var token in tokens) { + var term = token.Terminal; + if(_atLineStart || term == grammar.Eof) { + CheckOpeningClosingLists(token); + CheckTableStatus(token); + if (term == grammar.Eof) break; + } + if (term is WikiTerminalBase) + ProcessWikiToken(token); + else if(term == grammar.NewLine) { + ProcessNewLine(token); + } else //non-wike element and not new line + _output.Append(HtmlEncode(token.ValueString)); + _atLineStart = term == grammar.NewLine; //set for the next token + }//foreach token + + _output.AppendLine(); + _output.AppendLine(""); + return _output.ToString(); + }//method + + private void ProcessNewLine(Token token) { + if (_insideTable & !_insideCell) return; //ignore it in one special case - to make output look nicer + if (_currentHeader != null) + _output.AppendLine(_currentHeader.CloseHtmlTag); + else + _output.AppendLine("
"); + _currentHeader = null; + }//method + + private void ProcessWikiToken(Token token) { + //we check that token actually contains some chars - to allow "invisible spaces" after last table tag + if(_lastTableTag != null && !_insideCell && token.ValueString.Trim().Length > 0) { + _output.Append(_lastTableTag.OpenHtmlTag); + _insideCell = true; + } + var wikiTerm = token.Terminal as WikiTerminalBase; + switch(wikiTerm.TermType) { + case WikiTermType.Element: + _output.Append(wikiTerm.OpenHtmlTag); + _output.Append(wikiTerm.CloseHtmlTag); + break; + case WikiTermType.Format: + ProcessFormatTag(token); + break; + case WikiTermType.Heading: + case WikiTermType.List: + _output.Append(wikiTerm.OpenHtmlTag); + _currentHeader = wikiTerm; + break; + case WikiTermType.Block: + ProcessWikiBlockTag(token); + break; + case WikiTermType.Text: + _output.Append(HtmlEncode(token.ValueString)); + break; + case WikiTermType.Table: + if (_insideCell) + _output.Append(_lastTableTag.CloseHtmlTag); //write out or + //We do not write opening tag immediately: we need to know if it is the last table tag on the line. + // if yes, we don't write it at all; _lastTableTag will be cleared when we start new line + _lastTableTag = wikiTerm as WikiTagTerminal; + _insideCell = false; + break; + } + } + + private void ProcessFormatTag(Token token) { + var term = token.Terminal as WikiTerminalBase; + bool value; + bool isOn = _flags.TryGetValue(term, out value) && value; + if (isOn) + _output.Append(term.CloseHtmlTag); + else + _output.Append(term.OpenHtmlTag); + _flags[term] = !isOn; + } + + private void ProcessWikiBlockTag(Token token) { + var term = token.Terminal as WikiBlockTerminal; + string template; + string[] segments; + + switch(term.BlockType) { + case WikiBlockType.EscapedText: + case WikiBlockType.CodeBlock: + _output.Append(term.OpenHtmlTag); + _output.Append(HtmlEncode(token.ValueString)); + _output.AppendLine(term.CloseHtmlTag); + break; + case WikiBlockType.Anchor: + _output.Append(""); + break; + case WikiBlockType.LinkToAnchor: + _output.Append("" + HtmlEncode(token.ValueString) + ""); + break; + case WikiBlockType.Url: + case WikiBlockType.FileLink: + template = "{1}"; + segments = token.ValueString.Split('|'); + if(segments.Length > 1) + _output.Append(string.Format(template, segments[1], segments[0])); + else + _output.Append(string.Format(template, segments[0], segments[0])); + break; + case WikiBlockType.Image: + segments = token.ValueString.Split('|'); + switch(segments.Length){ + case 1: + template = ""; + _output.Append(string.Format(template, segments[0])); + break; + case 2: + template = "\"{0}\""; + _output.Append(string.Format(template, segments[0], segments[1])); + break; + case 3: + template = "\"{0}\""; + _output.Append(string.Format(template, segments[0], segments[1], segments[2])); + break; + }//switch segments.Length + break; + }//switch + }//method + + #region comments + /* Note: we allow mix of bulleted/numbered lists, so we can have bulleted list inside numbered item: + + # item 1 + ** bullet 1 + ** bullet 2 + # item 2 + + This is a bit different from codeplex rules - the bulletted list resets the numeration of items, so that "item 2" would + appear with number 1, not 2. While our handling seems more flexible, you can easily change the following method to + follow codeplex rules. */ + #endregion + //Called at the start of each line (after NewLine) + private void CheckOpeningClosingLists(Token token) { + var nextLevel = 0; + var wikiTerm = token.Terminal as WikiTerminalBase; + if(wikiTerm != null && wikiTerm.TermType == WikiTermType.List) + nextLevel = wikiTerm.OpenTag.Length; + //for codeplex-style additionally check that the control char is the same (# vs *). If not, unwind the levels + if (CurrentListLevel == nextLevel) return; //it is at the same level; + //New list begins + if(nextLevel > CurrentListLevel) { + _output.Append(wikiTerm.ContainerOpenHtmlTag); + _openLists.Push(wikiTerm); + return; + } + //One or more lists end + while(nextLevel < CurrentListLevel) { + var oldTerm =_openLists.Pop(); + _output.Append( oldTerm.ContainerCloseHtmlTag); + }//while + }//method + + //Called at the start of each line + private void CheckTableStatus(Token token) { + var wikiTerm = token.Terminal as WikiTerminalBase; + bool isTableTag = wikiTerm != null && wikiTerm.TermType == WikiTermType.Table; + if ( !_insideTable && !isTableTag) return; + _insideCell = false; //if we are at line start, drop this flag + _lastTableTag = null; + //New table begins + if(!_insideTable && isTableTag) { + _output.AppendLine(""); + _output.Append(""); + _insideTable = true; + return; + } + //existing table continues + if(_insideTable && isTableTag) { + _output.AppendLine(""); + _output.Append(""); + return; + } + //existing table ends + if(_insideTable && !isTableTag) { + _output.AppendLine(""); + _output.AppendLine("
"); + _insideTable = false; + return; + } + }//method + + }//class + +}//namespace diff --git a/Irony.Samples/app.config b/Irony.Samples/app.config new file mode 100644 index 0000000..48ad980 --- /dev/null +++ b/Irony.Samples/app.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Irony.Tests/040.Irony.Tests.VsTest.2010.csproj b/Irony.Tests/040.Irony.Tests.VsTest.2010.csproj new file mode 100644 index 0000000..b9999a4 --- /dev/null +++ b/Irony.Tests/040.Irony.Tests.VsTest.2010.csproj @@ -0,0 +1,138 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {0CFA8CEE-0110-49C1-93E5-CB7BE3A5716B} + Library + Properties + Irony.Tests + Irony.Tests.VSTest + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + + + + + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {AD263C0B-99D3-40A9-9DBF-9086CC524A0B} + 020.Irony.Samples.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Irony.Tests/050.Irony.Tests.NUnit.2010.csproj b/Irony.Tests/050.Irony.Tests.NUnit.2010.csproj new file mode 100644 index 0000000..147f007 --- /dev/null +++ b/Irony.Tests/050.Irony.Tests.NUnit.2010.csproj @@ -0,0 +1,143 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {1A98BEDA-DF53-49B7-A366-2ED7606036BD} + Library + Properties + Irony.Tests + Irony.Tests.NUnit + v4.0 + 512 + + + + + + + + + + + 3.5 + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + true + full + false + bin\Debug\ + TRACE;DEBUG;USE_NUNIT + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE;USE_NUNIT + prompt + 4 + AllRules.ruleset + + + + ..\..\..\_Vita_Related\NUnit-2.5.7.10213\bin\net-2.0\nunit.framework.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {AD263C0B-99D3-40A9-9DBF-9086CC524A0B} + 020.Irony.Samples.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Irony.Tests/050.Irony.Tests.NUnit.2010.csproj.vspscc b/Irony.Tests/050.Irony.Tests.NUnit.2010.csproj.vspscc new file mode 100644 index 0000000..feffdec --- /dev/null +++ b/Irony.Tests/050.Irony.Tests.NUnit.2010.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Irony.Tests/CommentTerminalTests.cs b/Irony.Tests/CommentTerminalTests.cs new file mode 100644 index 0000000..b03b939 --- /dev/null +++ b/Irony.Tests/CommentTerminalTests.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class CommentTerminalTests { + + [TestMethod] + public void TestCommentTerminal() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(new CommentTerminal("Comment", "/*", "*/")); + token = parser.ParseInput("/* abc */"); + Assert.IsTrue(token.Category == TokenCategory.Comment, "Failed to read comment"); + + parser = TestHelper.CreateParser(new CommentTerminal("Comment", "//", "\n")); + token = parser.ParseInput("// abc \n "); + Assert.IsTrue(token.Category == TokenCategory.Comment, "Failed to read line comment"); + + }//method + + }//class +}//namespace diff --git a/Irony.Tests/DataLiteralsTests.cs b/Irony.Tests/DataLiteralsTests.cs new file mode 100644 index 0000000..6eb1e4d --- /dev/null +++ b/Irony.Tests/DataLiteralsTests.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class DataLiteralsTests { + + [TestMethod] + public void TestDataLiterals() { + Parser parser; Token token; + Terminal term; + + // FixedLengthLiteral --------------------------------------------------------- + term = new FixedLengthLiteral("fixedLengthInteger", 2, TypeCode.Int32); + parser = TestHelper.CreateParser(term, null); + + token = parser.ParseInput("1200"); + Assert.IsTrue(token.Value != null, "Failed to parse fixed-length integer."); + Assert.IsTrue((int)token.Value == 12, "Failed to parse fixed-length integer - result value does not match."); + + term = new FixedLengthLiteral("fixedLengthString", 2, TypeCode.String); + parser = TestHelper.CreateParser(term); + token = parser.ParseInput("abcd", useTerminator: false); + Assert.IsTrue(token != null && token.Value != null, "Failed to parse fixed-length string."); + Assert.IsTrue((string)token.Value == "ab", "Failed to parse fixed-length string - result value does not match"); + + // DsvLiteral ---------------------------------------------------------------- + term = new DsvLiteral("DsvInteger", TypeCode.Int32, ","); + parser = TestHelper.CreateParser(term); + token = parser.ParseInput("12,"); + Assert.IsTrue(token != null && token.Value != null, "Failed to parse CSV integer."); + Assert.IsTrue((int)token.Value == 12, "Failed to parse CSV integer - result value does not match."); + + term = new DsvLiteral("DsvInteger", TypeCode.String, ","); + parser = TestHelper.CreateParser(term); + token = parser.ParseInput("ab,"); + Assert.IsTrue(token != null && token.Value != null, "Failed to parse CSV string."); + Assert.IsTrue((string)token.Value == "ab", "Failed to parse CSV string - result value does not match."); + + // QuotedValueLiteral ---------------------------------------------------------------- + term = new QuotedValueLiteral ("QVDate", "#", TypeCode.DateTime); + parser = TestHelper.CreateParser(term); + token = parser.ParseInput("#11/15/2009#"); + Assert.IsTrue(token != null && token.Value != null, "Failed to parse quoted date."); + Assert.IsTrue((DateTime)token.Value == new DateTime(2009, 11, 15), "Failed to parse quoted date - result value does not match."); + + }//method + + + }//class + +}//namespace diff --git a/Irony.Tests/ErrorRecoveryTests.cs b/Irony.Tests/ErrorRecoveryTests.cs new file mode 100644 index 0000000..421587b --- /dev/null +++ b/Irony.Tests/ErrorRecoveryTests.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class ErrorRecoveryTests { + + #region Grammars + //A simple grammar for language consisting of simple assignment statements: x=y + z; z= t + m; + public class ErrorRecoveryGrammar : Grammar { + public ErrorRecoveryGrammar() { + var id = new IdentifierTerminal("id"); + var expr = new NonTerminal("expr"); + var stmt = new NonTerminal("stmt"); + var stmtList = new NonTerminal("stmt"); + + base.Root = stmtList; + stmtList.Rule = MakeStarRule(stmtList, stmt); + stmt.Rule = id + "=" + expr + ";"; + stmt.ErrorRule = SyntaxError + ";"; + expr.Rule = id | id + "+" + id; + } + }// class + + #endregion + + [TestMethod] + public void TestErrorRecovery() { + + var grammar = new ErrorRecoveryGrammar(); + var parser = new Parser(grammar); + TestHelper.CheckGrammarErrors(parser); + + //correct sample + var parseTree = parser.Parse("x = y; y = z + m; m = n;"); + Assert.IsFalse(parseTree.HasErrors(), "Unexpected parse errors in correct source sample."); + + parseTree = parser.Parse("x = y; m = = d ; y = z + m; x = z z; m = n;"); + Assert.AreEqual(2, parseTree.ParserMessages.Count, "Invalid # of errors."); + + } + + + }//class +}//namespace diff --git a/Irony.Tests/EvaluatorTests.cs b/Irony.Tests/EvaluatorTests.cs new file mode 100644 index 0000000..e2ab49e --- /dev/null +++ b/Irony.Tests/EvaluatorTests.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Interpreter; +using Irony.Interpreter.Evaluator; + +namespace Irony.Tests { + +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + + [TestClass] + public class EvaluatorTests { + + [TestMethod] + public void TestEvaluator_Ops() { + var eval = new ExpressionEvaluator(); + string script; + object result; + + //Simple computation + script = "2*3"; + result = eval.Evaluate(script); + Assert.AreEqual(6, result, "Unexpected computation result"); + + //Using variables + script = @" +x=2 +y=4 +x * y +"; + result = eval.Evaluate(script); + Assert.AreEqual(8, result, "Unexpected computation result"); + + //Operator precedence + script = @" +x=2 +y=3 +x + y * 5 +"; + result = eval.Evaluate(script); + Assert.AreEqual(17, result, "Unexpected computation result"); + + //parenthesis + script = @" +x=3 +y=2 +1 + (x - y) * 5 +"; + result = eval.Evaluate(script); + Assert.AreEqual(6, result, "Unexpected computation result"); + + //strings + script = @" +x='2' +y='3' +x + y + 4 +"; + result = eval.Evaluate(script); + Assert.AreEqual("234", result, "Unexpected computation result"); + + //string with embedded expressions + script = @" +x = 4 +y = 7 +'#{x} * #{y} = #{x * y}' +"; + result = eval.Evaluate(script); + Assert.AreEqual("4 * 7 = 28", result, "Unexpected computation result"); + + //various operators + script = @" +x = 1 + 2 * 3 # =7 +y = --x # = 6 +z = x * 1.5 # = 9 +z -= y # = 3 +"; + result = eval.Evaluate(script); + Assert.AreEqual(3.0, (double) result, 0.0001, "Unexpected computation result"); + + //&&, || operators + script = @"x = (1 > 0) || (1/0)"; + result = eval.Evaluate(script); + Assert.AreEqual(true, result, "Unexpected computation result"); + + //Operator precedence test + script = @"2+3*3*3"; + result = eval.Evaluate(script); + Assert.AreEqual(29, result, "Operator precedence test failed."); + + script = @"x = (1 < 0) && (1/0)"; + result = eval.Evaluate(script); + Assert.AreEqual(false, result, "Unexpected computation result"); + } + + [TestMethod] + public void TestEvaluator_BuiltIns() { + var eval = new ExpressionEvaluator(); + string script; + object result; + + //Using methods imported from System.Math class + script = @"abs(-1.0) + Log10(100.0) + sqrt(9) + floor(4.5) + sin(PI/2)"; + result = eval.Evaluate(script); + Assert.IsTrue(result is double, "Result is not double."); + Assert.AreEqual(11.0, (double) result, 0.001, "Unexpected computation result"); + + //Using methods imported from System.Environment + script = @"report = '#{MachineName}-#{OSVersion}-#{UserName}'"; + result = eval.Evaluate(script); + var expected = string.Format("{0}-{1}-{2}", Environment.MachineName, Environment.OSVersion, Environment.UserName); + Assert.AreEqual(expected, result, "Unexpected computation result"); + + //Using special built-in methods print and format + eval.ClearOutput(); + script = @"print(format('{0} * {1} = {2}', 3, 4, 3 * 4))"; + eval.Evaluate(script); + result = eval.GetOutput(); + Assert.AreEqual("3 * 4 = 12\r\n", result, "Unexpected computation result"); + + //Add custom built-in method SayHello and test it + eval.Runtime.BuiltIns.AddMethod(SayHello, "SayHello", 1, 1, "name"); + script = @"SayHello('John')"; + result = eval.Evaluate(script); + Assert.AreEqual("Hello, John!", result, "Unexpected computation result"); + } + + //custom built-in method added to evaluator in Built-in tests + public static string SayHello(ScriptThread thread, object[] args) { + return "Hello, " + args[0] + "!"; + } + + [TestMethod] + public void TestEvaluator_Iif() { + var eval = new ExpressionEvaluator(); + string script; + object result; + + //Test '? :' operator + script = @"1 < 0 ? 1/0 : 'false' "; // Notice that (1/0) is not evaluated + result = eval.Evaluate(script); + Assert.AreEqual("false", result, "Unexpected computation result"); + + //Test iif special form + script = @"iif(1 > 0, 'true', 1/0) "; //Notice that (1/0) is not evaluated + result = eval.Evaluate(script); + Assert.AreEqual("true", result, "Unexpected computation result"); + } + + [TestMethod] + public void TestEvaluator_MemberAccess() { + var eval = new ExpressionEvaluator(); + eval.Globals["foo"] = new Foo(); + string script; + object result; + + //Test access to field, prop, calling a method + script = @"foo.Field + ',' + foo.Prop + ',' + foo.GetStuff()"; + result = eval.Evaluate(script); + Assert.AreEqual("F,P,S", result, "Unexpected computation result"); + + script = @" +foo.Field = 'FF' +foo.Prop = 'PP' +R = foo.Field + foo.Prop "; + result = eval.Evaluate(script); + Assert.AreEqual("FFPP", result, "Unexpected computation result"); + + //Test access to indexed properties + script = @"foo[3]"; + result = eval.Evaluate(script); + Assert.AreEqual("#3", result, "Unexpected computation result"); + + script = @"foo['a']"; + result = eval.Evaluate(script); + Assert.AreEqual("V-a", result, "Unexpected computation result"); + + // Test with string literal + script = @" '0123'.Substring(1) + 'abcd'.Length "; + result = eval.Evaluate(script); + Assert.AreEqual("1234", result, "Unexpected computation result"); + } + + //A class used for member access testing + public class Foo { + public string Field = "F"; + public string Prop { get; set; } + + public Foo() { + Prop = "P"; + } + public string GetStuff() { + return "S"; + } + public string this[int i] { + get { return "#" + i; } + set { } + } + public string this[string key] { + get { return "V-" + key; } + set { } + } + } + + [TestMethod] + public void TestEvaluator_ArrayDictDataRow() { + var eval = new ExpressionEvaluator(); + //Create an array, a dictionary and a data row and add them to Globals + eval.Globals["primes"] = new int[] { 3, 5, 7, 11, 13 }; + var nums = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + nums["one"] = "1"; + nums["two"] = "2"; + nums["three"] = "3"; + eval.Globals["nums"] = nums; + var t = new System.Data.DataTable(); + t.Columns.Add("Name", typeof(string)); + t.Columns.Add("Age", typeof(int)); + var row = t.NewRow(); + row["Name"] = "John"; + row["Age"] = 30; + eval.Globals["row"] = row; + + string script; + object result; + + //Test array + script = @"primes[3]"; + result = eval.Evaluate(script); + Assert.AreEqual(11, result, "Unexpected computation result"); + script = @" +primes[3] = 12345 +primes[3]"; + result = eval.Evaluate(script); + Assert.AreEqual(12345, result, "Unexpected computation result"); + + //Test dict + script = @"nums['three'] + nums['two'] + nums['one']"; + result = eval.Evaluate(script); + Assert.AreEqual("321", result, "Unexpected computation result"); + script = @" +nums['two'] = '22' +nums['three'] + nums['two'] + nums['one'] +"; + result = eval.Evaluate(script); + Assert.AreEqual("3221", result, "Unexpected computation result"); + + //Test data row + script = @"row['Name'] + ', ' + row['age']"; + result = eval.Evaluate(script); + Assert.AreEqual("John, 30", result, "Unexpected computation result"); + script = @" +row['Name'] = 'Jon' +row['Name'] + ', ' + row['age']"; + result = eval.Evaluate(script); + Assert.AreEqual("Jon, 30", result, "Unexpected computation result"); + } + + + }//class +}//namespace diff --git a/Irony.Tests/FreeTextLiteralTests.cs b/Irony.Tests/FreeTextLiteralTests.cs new file mode 100644 index 0000000..f435b42 --- /dev/null +++ b/Irony.Tests/FreeTextLiteralTests.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class FreeTextLiteralTests { + //A special grammar that does not skip whitespace + class FreeTextLiteralTestGrammar : Grammar { + public string Terminator = "END"; + public FreeTextLiteralTestGrammar(Terminal terminal) + : base(caseSensitive: true) { + var rule = new BnfExpression(terminal); + MarkReservedWords(Terminator); + rule += Terminator; + base.Root = new NonTerminal("Root"); + Root.Rule = rule; + } + + //Overrides base method, effectively suppressing skipping whitespaces + public override void SkipWhitespace(ISourceStream source) { + return; + } + }//class + + private Parser CreateParser(Terminal terminal) { + var grammar = new FreeTextLiteralTestGrammar(terminal); + return new Parser(grammar); + } + private Token GetFirst(ParseTree tree) { + return tree.Tokens[0]; + } + + + //The following test method and a fix are contributed by ashmind codeplex user + [TestMethod] + public void TestFreeTextLiteral_Escapes() { + Parser parser; Token token; + + //Escapes test + var term = new FreeTextLiteral("FreeText", ",", ")"); + term.Escapes.Add(@"\\", @"\"); + term.Escapes.Add(@"\,", @","); + term.Escapes.Add(@"\)", @")"); + + parser = this.CreateParser(term); + token = GetFirst(parser.Parse(@"abc\\de\,\)fg,")); + Assert.IsNotNull(token, "Failed to produce a token on valid string."); + Assert.AreEqual(term, token.Terminal, "Failed to scan a string - invalid Terminal in the returned token."); + Assert.AreEqual(token.Value.ToString(), @"abc\de,)fg", "Failed to scan a string"); + + term = new FreeTextLiteral("FreeText", FreeTextOptions.AllowEof, ";"); + parser = this.CreateParser(term); + token = GetFirst(parser.Parse(@"abcdefg")); + Assert.IsNotNull(token, "Failed to produce a token for free text ending at EOF."); + Assert.AreEqual(term, token.Terminal, "Failed to scan a free text ending at EOF - invalid Terminal in the returned token."); + Assert.AreEqual(token.Value.ToString(), @"abcdefg", "Failed to scan a free text ending at EOF"); + + //The following test method and a fix are contributed by ashmind codeplex user + //VAR + //MESSAGE:STRING80; + //(*_ORError Message*) + //END_VAR + term = new FreeTextLiteral("varContent", "END_VAR"); + term.Firsts.Add("VAR"); + parser = this.CreateParser(term); + token = GetFirst(parser.Parse("VAR\r\nMESSAGE:STRING80;\r\n(*_ORError Message*)\r\nEND_VAR")); + Assert.IsNotNull(token, "Failed to produce a token on valid string."); + Assert.AreEqual(term, token.Terminal, "Failed to scan a string - invalid Terminal in the returned token."); + Assert.AreEqual(token.ValueString, "\r\nMESSAGE:STRING80;\r\n(*_ORError Message*)\r\n", "Failed to scan a string"); + + term = new FreeTextLiteral("freeText", FreeTextOptions.AllowEof); + parser = this.CreateParser(term); + token = GetFirst(parser.Parse(" ")); + Assert.IsNotNull(token, "Failed to produce a token on valid string."); + Assert.AreEqual(term, token.Terminal, "Failed to scan a string - invalid Terminal in the returned token."); + Assert.AreEqual(token.ValueString, " ", "Failed to scan a string"); + + } + + }//class +}//namespace diff --git a/Irony.Tests/HeredocTerminalTests.cs b/Irony.Tests/HeredocTerminalTests.cs new file mode 100644 index 0000000..a448d8c --- /dev/null +++ b/Irony.Tests/HeredocTerminalTests.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + //Currently not used, HereDocTerminal needs to be finished + + /// + /// Summary description for HeredocTerminalTests + /// + [TestClass] + public class HeredocTerminalTests { + private TestContext testContextInstance; + private Parser _p; + + private class HereDocTestGrammar : Grammar { + public HereDocTestGrammar() + : base(true) { + var heredoc = new HereDocTerminal("HereDoc", "<<", HereDocOptions.None); + heredoc.AddSubType("<<-", HereDocOptions.AllowIndentedEndToken); + var @string = new StringLiteral("string", "\""); + var program = new NonTerminal("program"); + program.Rule = heredoc + @"+" + @string + this.NewLine + @"+" + @string + | heredoc + @"+" + heredoc + @"+" + @string + this.NewLine + | heredoc + @"+" + @string + this.NewLine + | heredoc + @"+" + @string + @"+" + heredoc + | heredoc + @"+" + heredoc + | heredoc; + this.Root = program; + this.MarkPunctuation("+"); + } + } + + private string NormalizeParseTree(ParseTree tree) { + StringBuilder fullString = new StringBuilder(); + foreach (ParseTreeNode node in tree.Root.ChildNodes) { + fullString.Append(node.Token.Value); + } + fullString = fullString.Replace("\r\n", "\\n"); + fullString = fullString.Replace("\n", "\\n"); + return fullString.ToString(); + } + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext { + get { + return testContextInstance; + } + set { + testContextInstance = value; + } + } + + #region Additional test attributes + // + // You can use the following additional attributes as you write your tests: + // + // Use ClassInitialize to run code before running the first test in the class + // [ClassInitialize()] + // public static void MyClassInitialize(TestContext testContext) { } + // + // Use ClassCleanup to run code after all tests in a class have run + // [ClassCleanup()] + // public static void MyClassCleanup() { } + // + // Use TestInitialize to run code before running each test + // [TestInitialize()] + // public void MyTestInitialize() { } + // + // Use TestCleanup to run code after each test has run + // [TestCleanup()] + // public void MyTestCleanup() { } + // + #endregion + + [TestInitialize] + public void HereDocSetup() { + HereDocTestGrammar grammar = new HereDocTestGrammar(); + _p = new Parser(grammar); + _p.Context.SetOption(ParseOptions.TraceParser, true); + } + + [TestMethod] + public void TestHereDocLiteral() { + var term = new HereDocTerminal("Heredoc","<<",HereDocOptions.None); + parser = TestHelper.CreateParser(term); + token = parser.ParseToken(@"<\n"" +This is the beginning. +It is two lines long. +HELLO ++ ""And now it's over!"""); + Assert.AreEqual(@"This is the beginning.\nIt is two lines long.\n<--- this is the middle --->\nAnd now it's over!", NormalizeParseTree(tree), "Incorrectly parsed heredoc."); + } + + [TestMethod] + public void TestHereDocParseHereDocStringHereDoc() { + ParseTree tree = _p.Parse(@"<\n"" + <\nAnd now it's over!\nBut we have three lines left.\nNow two more lines.\nOops, last line! :(", NormalizeParseTree(tree), "Incorrectly parsed heredoc."); + } + + [TestMethod] + public void TestHereDocParseHereDocHereDoc() { + ParseTree tree = _p.Parse(@"<"" +This is the beginning. +HELLO +And this is the middle. +MIDDLE"); + Assert.AreEqual(@"This is the beginning.\nAnd this is the middle.\n<--- And now it's over --->", NormalizeParseTree(tree), "Incorrectly parsed heredoc."); + } + + [TestMethod] + public void TestHereDocParseHereDocString() { + ParseTree tree = _p.Parse(@"<"" +This is the beginning. +HELLO"); + Assert.AreEqual(@"This is the beginning.\n<--- this is the end --->", NormalizeParseTree(tree), "Incorrectly parsed heredoc."); + } + + [TestMethod] + public void TestHereDocParseIndentHereDocStringHereDoc() { + ParseTree tree = _p.Parse(@"<<-BEGIN + ""<--- middle --->\n"" + <<-END +This is the beginning: + BEGIN +And now it is over! + END"); + Assert.AreEqual(@"This is the beginning:\n<--- middle --->\nAnd now it is over!", NormalizeParseTree(tree), "Incorrectly parsed heredoc."); + } + } +} diff --git a/Irony.Tests/IdentifierTerminalTests.cs b/Irony.Tests/IdentifierTerminalTests.cs new file mode 100644 index 0000000..eee45df --- /dev/null +++ b/Irony.Tests/IdentifierTerminalTests.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class IdentifierTerminalTests { + + [TestMethod] + public void TestIdentifier_CSharp() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(TerminalFactory.CreateCSharpIdentifier("Identifier")); + token = parser.ParseInput("x "); + Assert.IsTrue(token.Terminal.Name == "Identifier", "Failed to parse identifier"); + Assert.IsTrue((string)token.Value == "x", "Failed to parse identifier"); + token = parser.ParseInput("_a01 "); + Assert.IsTrue(token.Terminal.Name == "Identifier", "Failed to parse identifier starting with _"); + Assert.IsTrue((string)token.Value == "_a01", "Failed to parse identifier starting with _"); + + token = parser.ParseInput("0abc "); + Assert.IsTrue(token.IsError(), "Erroneously recognized an identifier."); + + token = parser.ParseInput(@"_\u0061bc "); + Assert.IsTrue(token.Terminal.Name == "Identifier", "Failed to parse identifier starting with _"); + Assert.IsTrue((string)token.Value == "_abc", "Failed to parse identifier containing escape sequence \\u"); + + token = parser.ParseInput(@"a\U00000062c_ "); + Assert.IsTrue(token.Terminal.Name == "Identifier", "Failed to parse identifier starting with _"); + Assert.IsTrue((string)token.Value == "abc_", "Failed to parse identifier containing escape sequence \\U"); + }//method + + [TestMethod] + public void TestIdentifier_CaseRestrictions() { + Parser parser; Token token; + + var id = new IdentifierTerminal("identifier"); + id.CaseRestriction = CaseRestriction.None; + parser = TestHelper.CreateParser(id); + + token = parser.ParseInput("aAbB"); + Assert.IsTrue(token != null, "Failed to scan an identifier aAbB."); + + id.CaseRestriction = CaseRestriction.FirstLower; + parser = TestHelper.CreateParser(id); + token = parser.ParseInput("BCD"); + Assert.IsTrue(token.IsError(), "Erroneously recognized an identifier BCD with FirstLower restriction."); + token = parser.ParseInput("bCd "); + Assert.IsTrue(token != null && token.ValueString == "bCd", "Failed to scan identifier bCd with FirstLower restriction."); + + id.CaseRestriction = CaseRestriction.FirstUpper; + parser = TestHelper.CreateParser(id); + token = parser.ParseInput("cDE"); + Assert.AreEqual(TokenCategory.Error, token.Category, "Erroneously recognized an identifier cDE with FirstUpper restriction."); + token = parser.ParseInput("CdE"); + Assert.IsTrue(token != null && token.ValueString == "CdE", "Failed to scan identifier CdE with FirstUpper restriction."); + + id.CaseRestriction = CaseRestriction.AllLower; + parser = TestHelper.CreateParser(id); + token = parser.ParseInput("DeF"); + Assert.IsTrue(token.IsError(), "Erroneously recognized an identifier DeF with AllLower restriction."); + token = parser.ParseInput("def"); + Assert.IsTrue(token != null && token.ValueString == "def", "Failed to scan identifier def with AllLower restriction."); + + id.CaseRestriction = CaseRestriction.AllUpper; + parser = TestHelper.CreateParser(id); + token = parser.ParseInput("EFg "); + Assert.IsTrue(token.IsError(), "Erroneously recognized an identifier EFg with AllUpper restriction."); + token = parser.ParseInput("EFG"); + Assert.IsTrue(token != null && token.ValueString == "EFG", "Failed to scan identifier EFG with AllUpper restriction."); + }//method + + + }//class +}//namespace + + diff --git a/Irony.Tests/IntegrationTests.cs b/Irony.Tests/IntegrationTests.cs new file mode 100644 index 0000000..156be83 --- /dev/null +++ b/Irony.Tests/IntegrationTests.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; +using Irony.Interpreter.Ast; + +//Tests of Visual Studio integration functionality + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + public class IntegrationTestGrammar : Grammar { + public IntegrationTestGrammar() { + var comment = new CommentTerminal("comment", "/*", "*/"); + base.NonGrammarTerminals.Add(comment); + var str = new StringLiteral("str", "'", StringOptions.AllowsLineBreak); + var stmt = new NonTerminal("stmt"); + stmt.Rule = str | Empty; + this.Root = stmt; + } + }//class + + [TestClass] + public class IntegrationTests { + Grammar _grammar; + LanguageData _language; + Scanner _scanner; + ParsingContext _context; + int _state; + + private void Init(Grammar grammar) { + _grammar = grammar; + _language = new LanguageData(_grammar); + var parser = new Parser(_language); + _scanner = parser.Scanner; + _context = parser.Context; + _context.Mode = ParseMode.VsLineScan; + } + + private void SetSource(string text) { + _scanner.VsSetSource(text, 0); + } + private Token Read() { + Token token = _scanner.VsReadToken(ref _state); + return token; + } + + [TestMethod] + public void TestIntegration_VsScanningComment() { + Init(new IntegrationTestGrammar()); + SetSource(" /* "); + Token token = Read(); + Assert.IsTrue(token.IsSet(TokenFlags.IsIncomplete), "Expected incomplete token (line 1)"); + token = Read(); + Assert.IsNull(token, "NULL expected"); + SetSource(" comment "); + token = Read(); + Assert.IsTrue(token.IsSet(TokenFlags.IsIncomplete), "Expected incomplete token (line 2)"); + token = Read(); + Assert.IsNull(token, "NULL expected"); + SetSource(" */ /*x*/"); + token = Read(); + Assert.IsFalse(token.IsSet(TokenFlags.IsIncomplete), "Expected complete token (line 3)"); + token = Read(); + Assert.IsFalse(token.IsSet(TokenFlags.IsIncomplete), "Expected complete token (line 3)"); + token = Read(); + Assert.IsNull(token, "Null expected."); + } + + [TestMethod] + public void TestIntegration_VsScanningString() { + Init(new IntegrationTestGrammar()); + SetSource(" 'abc"); + Token token = Read(); + Assert.IsTrue(token.ValueString == "abc", "Expected incomplete token 'abc' (line 1)"); + Assert.IsTrue(token.IsSet(TokenFlags.IsIncomplete), "Expected incomplete token (line 1)"); + token = Read(); + Assert.IsNull(token, "NULL expected"); + SetSource(" def "); + token = Read(); + Assert.IsTrue(token.ValueString == " def ", "Expected incomplete token ' def ' (line 2)"); + Assert.IsTrue(token.IsSet(TokenFlags.IsIncomplete), "Expected incomplete token (line 2)"); + token = Read(); + Assert.IsNull(token, "NULL expected"); + SetSource("ghi' 'x'"); + token = Read(); + Assert.IsTrue(token.ValueString == "ghi", "Expected token 'ghi' (line 3)"); + Assert.IsFalse(token.IsSet(TokenFlags.IsIncomplete), "Expected complete token (line 3)"); + token = Read(); + Assert.IsTrue(token.ValueString == "x", "Expected token 'x' (line 3)"); + Assert.IsFalse(token.IsSet(TokenFlags.IsIncomplete), "Expected complete token (line 3)"); + token = Read(); + Assert.IsNull(token, "Null expected."); + } + + }//class +}//namespace diff --git a/Irony.Tests/LineContinuationTests.cs b/Irony.Tests/LineContinuationTests.cs new file mode 100644 index 0000000..8a4c3b0 --- /dev/null +++ b/Irony.Tests/LineContinuationTests.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class LineContinuationTests { + + [TestMethod] + public void TestContinuationTerminal_Simple() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(new LineContinuationTerminal("LineContinuation", "\\")); + token = parser.ParseInput("\\\r\t"); + Assert.IsTrue(token.Category == TokenCategory.Outline, "Failed to read simple line continuation terminal"); + } + + [TestMethod] + public void TestContinuationTerminal_Default() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(new LineContinuationTerminal("LineContinuation")); + token = parser.ParseInput("_\r\n\t"); + Assert.IsTrue(token.Category == TokenCategory.Outline, "Failed to read default line continuation terminal"); + + token = parser.ParseInput("\\\v "); + Assert.IsTrue(token.Category == TokenCategory.Outline, "Failed to read default line continuation terminal"); + } + + [TestMethod] + public void TestContinuationTerminal_Complex() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(new LineContinuationTerminal("LineContinuation", @"\continue", @"\cont", "++CONTINUE++")); + token = parser.ParseInput("\\cont \r\n "); + Assert.IsTrue(token.Category == TokenCategory.Outline, "Failed to read complex line continuation terminal"); + + token = parser.ParseInput("++CONTINUE++\t\v"); + Assert.IsTrue(token.Category == TokenCategory.Outline, "Failed to read complex line continuation terminal"); + } + + [TestMethod] + public void TestContinuationTerminal_Incomplete() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(new LineContinuationTerminal("LineContinuation")); + token = parser.ParseInput("\\ garbage"); + Assert.IsTrue(token.Category == TokenCategory.Error, "Failed to read incomplete line continuation terminal"); + + parser = TestHelper.CreateParser(new LineContinuationTerminal("LineContinuation")); + token = parser.ParseInput("_"); + Assert.IsTrue(token.Category == TokenCategory.Error, "Failed to read incomplete line continuation terminal"); + } + } +} diff --git a/Irony.Tests/NumberLiteralTests.cs b/Irony.Tests/NumberLiteralTests.cs new file mode 100644 index 0000000..97ec647 --- /dev/null +++ b/Irony.Tests/NumberLiteralTests.cs @@ -0,0 +1,403 @@ +//Authors: Roman Ivantsov, Philipp Serr + +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class NumberLiteralTests { + + [TestMethod] + public void TestNumber_General() { + Parser parser; Token token; + + NumberLiteral number = new NumberLiteral("Number"); + number.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt }; + parser = TestHelper.CreateParser(number); + token = parser.ParseInput("123"); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == 123, "Failed to read int value"); + token = parser.ParseInput("123.4"); + Assert.IsTrue(Math.Abs(Convert.ToDouble(token.Value) - 123.4) < 0.000001, "Failed to read float value"); + //100 digits + string sbig = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + token = parser.ParseInput(sbig); + Assert.IsTrue(token.Value.ToString() == sbig, "Failed to read big integer value"); + }//method + + //The following "sign" test methods and a fix are contributed by ashmind codeplex user + [TestMethod] + public void TestNumber_SignedDoesNotMatchSingleMinus() { + Parser parser; Token token; + + var number = new NumberLiteral("number", NumberOptions.AllowSign); + parser = TestHelper.CreateParser(number); + token = parser.ParseInput("-"); + Assert.IsTrue(token.IsError(), "Parsed single '-' as a number value."); + } + + [TestMethod] + public void TestNumber_SignedDoesNotMatchSinglePlus() { + Parser parser; Token token; + + var number = new NumberLiteral("number", NumberOptions.AllowSign); + parser = TestHelper.CreateParser(number); + token = parser.ParseInput("+"); + Assert.IsTrue(token.IsError(), "Parsed single '+' as a number value."); + } + + [TestMethod] + public void TestNumber_SignedMatchesNegativeCorrectly() { + Parser parser; Token token; + + var number = new NumberLiteral("number", NumberOptions.AllowSign); + parser = TestHelper.CreateParser(number); + token = parser.ParseInput("-500"); + Assert.AreEqual(-500, token.Value, "Negative number was parsed incorrectly; expected: {0}, scanned: {1}", "-500", token.Value); + } + + [TestMethod] + public void TestNumber_CSharp() { + Parser parser; Token token; + + double eps = 0.0001; + parser = TestHelper.CreateParser(TerminalFactory.CreateCSharpNumber("Number")); + + //Simple integers and suffixes + token = parser.ParseInput("123 "); + CheckType(token, typeof(int)); + Assert.IsTrue(token.Details != null, "ScanDetails object not found in token."); + Assert.IsTrue((int)token.Value == 123, "Failed to read int value"); + + token = parser.ParseInput(Int32.MaxValue.ToString()); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == Int32.MaxValue, "Failed to read Int32.MaxValue."); + + token = parser.ParseInput(UInt64.MaxValue.ToString()); + CheckType(token, typeof(ulong)); + Assert.IsTrue((ulong)token.Value == UInt64.MaxValue, "Failed to read uint64.MaxValue value"); + + token = parser.ParseInput("123U "); + CheckType(token, typeof(UInt32)); + Assert.IsTrue((UInt32)token.Value == 123, "Failed to read uint value"); + + token = parser.ParseInput("123L "); + CheckType(token, typeof(long)); + Assert.IsTrue((long)token.Value == 123, "Failed to read long value"); + + token = parser.ParseInput("123uL "); + CheckType(token, typeof(ulong)); + Assert.IsTrue((ulong)token.Value == 123, "Failed to read ulong value"); + + //Hex representation + token = parser.ParseInput("0x012 "); + CheckType(token, typeof(Int32)); + Assert.IsTrue((Int32)token.Value == 0x012, "Failed to read hex int value"); + + token = parser.ParseInput("0x12U "); + CheckType(token, typeof(UInt32)); + Assert.IsTrue((UInt32)token.Value == 0x012, "Failed to read hex uint value"); + + token = parser.ParseInput("0x012L "); + CheckType(token, typeof(long)); + Assert.IsTrue((long)token.Value == 0x012, "Failed to read hex long value"); + + token = parser.ParseInput("0x012uL "); + CheckType(token, typeof(ulong)); + Assert.IsTrue((ulong)token.Value == 0x012, "Failed to read hex ulong value"); + + //Floating point types + token = parser.ParseInput("123.4 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #1"); + + token = parser.ParseInput("1234e-1 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 1234e-1) < eps, "Failed to read double value #2"); + + token = parser.ParseInput("12.34e+01 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #3"); + + token = parser.ParseInput("0.1234E3 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #4"); + + token = parser.ParseInput("123.4f "); + CheckType(token, typeof(float)); + Assert.IsTrue(Math.Abs((Single)token.Value - 123.4) < eps, "Failed to read float(single) value"); + + token = parser.ParseInput("123.4m "); + CheckType(token, typeof(decimal)); + Assert.IsTrue(Math.Abs((decimal)token.Value - 123.4m) < Convert.ToDecimal(eps), "Failed to read decimal value"); + + token = parser.ParseInput("123. ", useTerminator: false); //should ignore dot and read number as int. compare it to python numbers - see below + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == 123, "Failed to read int value with trailing dot"); + + //Quick parse + token = parser.ParseInput("1 "); + CheckType(token, typeof(int)); + //When going through quick parse path (for one-digit numbers), the NumberScanInfo record is not created and hence is absent in Attributes + Assert.IsTrue(token.Details == null, "Quick parse test failed: ScanDetails object is found in token - quick parse path should not produce this object."); + Assert.IsTrue((int)token.Value == 1, "Failed to read quick-parse value"); + } + + [TestMethod] + public void TestNumber_VB() { + Parser parser; Token token; + + double eps = 0.0001; + parser = TestHelper.CreateParser(TerminalFactory.CreateVbNumber("Number")); + + //Simple integer + token = parser.ParseInput("123 "); + CheckType(token, typeof(int)); + Assert.IsTrue(token.Details != null, "ScanDetails object not found in token."); + Assert.IsTrue((int)token.Value == 123, "Failed to read int value"); + + //Test all suffixes + token = parser.ParseInput("123S "); + CheckType(token, typeof(Int16)); + Assert.IsTrue((Int16)token.Value == 123, "Failed to read short value"); + + token = parser.ParseInput("123I "); + CheckType(token, typeof(Int32)); + Assert.IsTrue((Int32)token.Value == 123, "Failed to read int value"); + + token = parser.ParseInput("123% "); + CheckType(token, typeof(Int32)); + Assert.IsTrue((Int32)token.Value == 123, "Failed to read int value"); + + token = parser.ParseInput("123L "); + CheckType(token, typeof(long)); + Assert.IsTrue((long)token.Value == 123, "Failed to read long value"); + + token = parser.ParseInput("123& "); + CheckType(token, typeof(Int64)); + Assert.IsTrue((Int64)token.Value == 123, "Failed to read long value"); + + token = parser.ParseInput("123us "); + CheckType(token, typeof(UInt16)); + Assert.IsTrue((UInt16)token.Value == 123, "Failed to read ushort value"); + + token = parser.ParseInput("123ui "); + CheckType(token, typeof(UInt32)); + Assert.IsTrue((UInt32)token.Value == 123, "Failed to read uint value"); + + token = parser.ParseInput("123ul "); + CheckType(token, typeof(ulong)); + Assert.IsTrue((ulong)token.Value == 123, "Failed to read ulong value"); + + //Hex and octal + token = parser.ParseInput("&H012 "); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == 0x012, "Failed to read hex int value"); + + token = parser.ParseInput("&H012L "); + CheckType(token, typeof(long)); + Assert.IsTrue((long)token.Value == 0x012, "Failed to read hex long value"); + + token = parser.ParseInput("&O012 "); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == 10, "Failed to read octal int value"); //12(oct) = 10(dec) + + token = parser.ParseInput("&o012L "); + CheckType(token, typeof(long)); + Assert.IsTrue((long)token.Value == 10, "Failed to read octal long value"); + + //Floating point types + token = parser.ParseInput("123.4 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #1"); + + token = parser.ParseInput("1234e-1 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 1234e-1) < eps, "Failed to read double value #2"); + + token = parser.ParseInput("12.34e+01 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #3"); + + token = parser.ParseInput("0.1234E3 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #4"); + + token = parser.ParseInput("123.4R "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #5"); + + token = parser.ParseInput("123.4# "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #6"); + + token = parser.ParseInput("123.4f "); + CheckType(token, typeof(float)); + Assert.IsTrue(Math.Abs((Single)token.Value - 123.4) < eps, "Failed to read float(single) value"); + + token = parser.ParseInput("123.4! "); + CheckType(token, typeof(float)); + Assert.IsTrue(Math.Abs((Single)token.Value - 123.4) < eps, "Failed to read float(single) value"); + + token = parser.ParseInput("123.4D "); + CheckType(token, typeof(decimal)); + Assert.IsTrue(Math.Abs((decimal)token.Value - 123.4m) < Convert.ToDecimal(eps), "Failed to read decimal value"); + + token = parser.ParseInput("123.4@ "); + CheckType(token, typeof(decimal)); + Assert.IsTrue(Math.Abs((decimal)token.Value - 123.4m) < Convert.ToDecimal(eps), "Failed to read decimal value"); + + //Quick parse + token = parser.ParseInput("1 "); + CheckType(token, typeof(int)); + //When going through quick parse path (for one-digit numbers), the NumberScanInfo record is not created and hence is absent in Attributes + Assert.IsTrue(token.Details == null, "Quick parse test failed: ScanDetails object is found in token - quick parse path should not produce this object."); + Assert.IsTrue((int)token.Value == 1, "Failed to read quick-parse value"); + } + + + [TestMethod] + public void TestNumber_Python() { + Parser parser; Token token; + + double eps = 0.0001; + parser = TestHelper.CreateParser(TerminalFactory.CreatePythonNumber("Number")); + + //Simple integers and suffixes + token = parser.ParseInput("123 "); + CheckType(token, typeof(int)); + Assert.IsTrue(token.Details != null, "ScanDetails object not found in token."); + Assert.IsTrue((int)token.Value == 123, "Failed to read int value"); + + token = parser.ParseInput("123L "); + CheckType(token, typeof(long)); + Assert.IsTrue((long)token.Value == 123, "Failed to read long value"); + + //Hex representation + token = parser.ParseInput("0x012 "); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == 0x012, "Failed to read hex int value"); + + token = parser.ParseInput("0x012l "); //with small "L" + CheckType(token, typeof(long)); + Assert.IsTrue((long)token.Value == 0x012, "Failed to read hex long value"); + + //Floating point types + token = parser.ParseInput("123.4 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #1"); + + token = parser.ParseInput("1234e-1 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 1234e-1) < eps, "Failed to read double value #2"); + + token = parser.ParseInput("12.34e+01 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #3"); + + token = parser.ParseInput("0.1234E3 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #4"); + + token = parser.ParseInput(".1234 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 0.1234) < eps, "Failed to read double value with leading dot"); + + token = parser.ParseInput("123. "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.0) < eps, "Failed to read double value with trailing dot"); + + //Big integer + string sbig = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; //100 digits + token = parser.ParseInput(sbig); + Assert.IsTrue(token.Value.ToString() == sbig, "Failed to read big integer value"); + + //Quick parse + token = parser.ParseInput("1 "); + CheckType(token, typeof(int)); + Assert.IsTrue(token.Details == null, "Quick parse test failed: ScanDetails object is found in token - quick parse path should produce this object."); + Assert.IsTrue((int)token.Value == 1, "Failed to read quick-parse value"); + + } + + [TestMethod] + public void TestNumber_Scheme() { + Parser parser; Token token; + + double eps = 0.0001; + parser = TestHelper.CreateParser(TerminalFactory.CreateSchemeNumber("Number")); + + + //Just test default float value (double), and exp symbols (e->double, s->single, d -> double) + token = parser.ParseInput("123.4 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #1"); + + token = parser.ParseInput("1234e-1 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 1234e-1) < eps, "Failed to read single value #2"); + + token = parser.ParseInput("1234s-1 "); + CheckType(token, typeof(Single)); + Assert.IsTrue(Math.Abs((Single)token.Value - 1234e-1) < eps, "Failed to read single value #3"); + + token = parser.ParseInput("12.34d+01 "); + CheckType(token, typeof(double)); + Assert.IsTrue(Math.Abs((double)token.Value - 123.4) < eps, "Failed to read double value #4"); + }//method + + [TestMethod] + public void TestNumber_WithUnderscore() { + Parser parser; Token token; + + var number = new NumberLiteral("number", NumberOptions.AllowUnderscore); + parser = TestHelper.CreateParser(number); + + //Simple integers and suffixes + token = parser.ParseInput("1_234_567"); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == 1234567, "Failed to read int value with underscores."); + }//method + + + //There was a bug discovered in NumberLiteral - it cannot parse appropriately the int.MinValue value. + // This test ensures that the issue is fixed. + [TestMethod] + public void TestNumber_MinMaxValues() { + Parser parser; Token token; + + var number = new NumberLiteral("number", NumberOptions.AllowSign); + number.DefaultIntTypes = new TypeCode[] { TypeCode.Int32 }; + parser = TestHelper.CreateParser(number); + var s = int.MinValue.ToString(); + token = parser.ParseInput(s); + Assert.IsFalse(token.IsError(), "Failed to scan int.MinValue, scanner returned an error."); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == int.MinValue, "Failed to scan int.MinValue, scanned value does not match."); + s = int.MaxValue.ToString(); + token = parser.ParseInput(s); + Assert.IsFalse(token.IsError(), "Failed to scan int.MaxValue, scanner returned an error."); + CheckType(token, typeof(int)); + Assert.IsTrue((int)token.Value == int.MaxValue, "Failed to read int.MaxValue"); + }//method + + private void CheckType(Token token, Type type) { + Assert.IsNotNull(token, "TryMatch returned null, while token was expected."); + Type vtype = token.Value.GetType(); + Assert.IsTrue(vtype == type, "Invalid target type, expected " + type.ToString() + ", found: " + vtype); + } + + + }//class +}//namespace diff --git a/Irony.Tests/OperatorTests.cs b/Irony.Tests/OperatorTests.cs new file mode 100644 index 0000000..6a8c507 --- /dev/null +++ b/Irony.Tests/OperatorTests.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class OperatorTests { + + #region Grammars + public class OperatorGrammar : Grammar { + public OperatorGrammar() { + var id = new IdentifierTerminal("id"); + var binOp = new NonTerminal("binOp"); + var unOp = new NonTerminal("unOp"); + var expr = new NonTerminal("expr"); + var binExpr = new NonTerminal("binExpr"); + var unExpr = new NonTerminal("unExpr"); + + base.Root = expr; + expr.Rule = id | binExpr | unExpr; + binExpr.Rule = expr + binOp + expr; + binOp.Rule = ToTerm("+") | "-" | "*" | "/"; + unExpr.Rule = unOp + expr; + unOp.Rule = ToTerm("+") | "-"; + + RegisterOperators(10, "+", "-"); + RegisterOperators(20, "*", "/"); + MarkTransient(expr, binOp, unOp); + } + }//operator grammar class + + public class OperatorGrammarHintsOnTerms : Grammar { + public OperatorGrammarHintsOnTerms() { + var id = new IdentifierTerminal("id"); + var binOp = new NonTerminal("binOp"); + var unOp = new NonTerminal("unOp"); + var expr = new NonTerminal("expr"); + var binExpr = new NonTerminal("binExpr"); + var unExpr = new NonTerminal("unExpr"); + + base.Root = expr; + expr.Rule = id | binExpr | unExpr; + binExpr.Rule = expr + binOp + expr; + binOp.Rule = ToTerm("+") | "-" | "*" | "/"; + unExpr.Rule = unOp + expr; + var unOpHint = ImplyPrecedenceHere(30); // Force higher precedence than multiplication precedence + unOp.Rule = unOpHint + "+" | unOpHint + "-"; + RegisterOperators(10, "+", "-"); + RegisterOperators(20, "*", "/"); + MarkTransient(expr, binOp, unOp); + } + }//operator grammar class + + public class OperatorGrammarHintsOnNonTerms : Grammar { + public OperatorGrammarHintsOnNonTerms() { + var id = new IdentifierTerminal("id"); + var binOp = new NonTerminal("binOp"); + var unOp = new NonTerminal("unOp"); + var expr = new NonTerminal("expr"); + var binExpr = new NonTerminal("binExpr"); + var unExpr = new NonTerminal("unExpr"); + + base.Root = expr; + expr.Rule = id | binExpr | unExpr; + binExpr.Rule = expr + binOp + expr; + binOp.Rule = ToTerm("+") | "-" | "*" | "/"; + var unOpHint = ImplyPrecedenceHere(30); // Force higher precedence than multiplication precedence + unExpr.Rule = unOpHint + unOp + expr; + unOp.Rule = ToTerm("+") | "-"; + RegisterOperators(10, "+", "-"); + RegisterOperators(20, "*", "/"); + MarkTransient(expr, binOp, unOp); + } + }//operator grammar class + + #endregion + + [TestMethod] + public void TestOperatorPrecedence() { + + var grammar = new OperatorGrammar(); + var parser = new Parser(grammar); + TestHelper.CheckGrammarErrors(parser); + + var parseTree = parser.Parse("x + y * z"); + TestHelper.CheckParseErrors(parseTree); + Assert.IsTrue(parseTree.Root != null, "Root not found."); + Assert.IsTrue(parseTree.Root.Term.Name == "binExpr", "Expected binExpr."); + Assert.IsTrue(parseTree.Root.ChildNodes[1].Term.Name == "+", "Expected + operator."); //check that top operator is "+", not "*" + + parseTree = parser.Parse("x * y + z"); + TestHelper.CheckParseErrors(parseTree); + Assert.IsTrue(parseTree.Root != null, "Root not found."); + Assert.IsTrue(parseTree.Root.Term.Name == "binExpr", "Expected binExpr."); + Assert.IsTrue(parseTree.Root.ChildNodes[1].Term.Name == "+", "Expected + operator."); //check that top operator is "+", not "*" + + parseTree = parser.Parse("-x * y"); //should be interpreted as -(x*y), so top operator should be - + TestHelper.CheckParseErrors(parseTree); + Assert.IsTrue(parseTree.Root != null, "Root not found."); + Assert.IsTrue(parseTree.Root.Term.Name == "unExpr", "Expected unExpr."); + Assert.IsTrue(parseTree.Root.ChildNodes[0].Term.Name == "-", "Expected - operator."); //check that top operator is "+", not "*" + + } + + //These tests check how implied precedence work. We use ImpliedPrecedenceHint to set precedence on unary +,- operators and make it + // higher than binary +,-. We make it even higher than * precedence, so that -x*y is interpreted as '(-x)*y', not like '-(x*y)' + // the second interpretation is chosen when there are no hints. + [TestMethod] + public void TestOperatorPrecedenceWithHints() { + + var grammar = new OperatorGrammarHintsOnTerms(); + var parser = new Parser(grammar); + TestHelper.CheckGrammarErrors(parser); + + var parseTree = parser.Parse("x + y * z"); + TestHelper.CheckParseErrors(parseTree); + Assert.IsTrue(parseTree.Root != null, "Root not found."); + Assert.IsTrue(parseTree.Root.Term.Name == "binExpr", "Expected binExpr."); + Assert.IsTrue(parseTree.Root.ChildNodes[1].Term.Name == "+", "Expected + operator."); //check that top operator is "+", not "*" + + parseTree = parser.Parse("-x * y"); //should be interpreted as (-x)*y, so top operator should be * + TestHelper.CheckParseErrors(parseTree); + Assert.IsTrue(parseTree.Root != null, "Root not found."); + Assert.IsTrue(parseTree.Root.Term.Name == "binExpr", "Expected binExpr."); + Assert.IsTrue(parseTree.Root.ChildNodes[1].Term.Name == "*", "Expected * operator."); //check that top operator is "+", not "*" + + var grammar2 = new OperatorGrammarHintsOnNonTerms(); + parser = new Parser(grammar2); + TestHelper.CheckGrammarErrors(parser); + + parseTree = parser.Parse("x + y * z"); + TestHelper.CheckParseErrors(parseTree); + Assert.IsTrue(parseTree.Root != null, "Root not found."); + Assert.IsTrue(parseTree.Root.Term.Name == "binExpr", "Expected binExpr."); + Assert.IsTrue(parseTree.Root.ChildNodes[1].Term.Name == "+", "Expected + operator."); //check that top operator is "+", not "*" + + parseTree = parser.Parse("-x*y"); //should be interpreted as (-x)*y, so top operator should be * + TestHelper.CheckParseErrors(parseTree); + Assert.IsTrue(parseTree.Root != null, "Root not found."); + Assert.IsTrue(parseTree.Root.Term.Name == "binExpr", "Expected binExpr."); + Assert.IsTrue(parseTree.Root.ChildNodes[1].Term.Name == "*", "Expected * operator."); //check that top operator is "+", not "*" + + } + + }//class +}//namespace diff --git a/Irony.Tests/Properties/AssemblyInfo.cs b/Irony.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a93729a --- /dev/null +++ b/Irony.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Irony.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Irony.Tests")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4ee688fd-d07e-42ec-97eb-89aa354dcb66")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Irony.Tests/RegExLiteralTests.cs b/Irony.Tests/RegExLiteralTests.cs new file mode 100644 index 0000000..ff54591 --- /dev/null +++ b/Irony.Tests/RegExLiteralTests.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class RegexLiteralTests { + + //The following test method and a fix are contributed by ashmind codeplex user + [TestMethod] + public void TestRegExLiteral() { + Parser parser; Token token; + + var term = new RegexLiteral("RegEx"); + parser = TestHelper.CreateParser(term); + token = parser.ParseInput(@"/abc\\\/de/gm "); + Assert.IsNotNull(token, "Failed to produce a token on valid string."); + Assert.AreEqual(term, token.Terminal, "Failed to scan a string - invalid Terminal in the returned token."); + Assert.IsNotNull(token.Value, "Token Value field is null - should be Regex object."); + var regex = token.Value as Regex; + Assert.IsNotNull(regex, "Failed to create Regex object."); + var match = regex.Match(@"00abc\/de00"); + Assert.AreEqual(match.Index, 2, "Failed to match a regular expression"); + } + + }//class +}//namespace diff --git a/Irony.Tests/StringLiteralTests.cs b/Irony.Tests/StringLiteralTests.cs new file mode 100644 index 0000000..05530d5 --- /dev/null +++ b/Irony.Tests/StringLiteralTests.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class StringLiteralTests { + + + //handy option for stringLiteral tests: we use single quotes in test strings, and they are replaced by double quotes here + private static string ReplaceQuotes(string input) { + return input.Replace("'", "\""); + } + + //The following test method and a fix are contributed by ashmind codeplex user + [TestMethod] + public void TestString_QuoteJustBeforeEof() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(new StringLiteral("String", "'")); + token = parser.ParseInput(@"'"); + Assert.AreEqual(TokenCategory.Error, token.Terminal.Category, "Incorrect string was not parsed as syntax error."); + } + + + [TestMethod] + public void TestString_Python() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(TerminalFactory.CreatePythonString("String")); + //1. Single quotes + token = parser.ParseInput(@"'00\a\b\t\n\v\f\r\'\\00' "); + Assert.IsTrue((string)token.Value == "00\a\b\t\n\v\f\r\'\\00", "Failed to process escaped characters."); + token = parser.ParseInput("'abcd\nefg' "); + Assert.IsTrue(token.IsError(), "Failed to detect erroneous multi-line string."); + token = parser.ParseInput("'''abcd\nefg''' "); + Assert.IsTrue((string)token.Value == "abcd\nefg", "Failed to process line break in triple-quote string."); + token = parser.ParseInput(@"'''abcd\" + "\n" + "efg''' "); + Assert.IsTrue((string)token.Value == "abcd\nefg", "Failed to process escaped line-break char."); + token = parser.ParseInput(@"r'00\a\b\t\n\v\f\r00' "); + Assert.IsTrue((string)token.Value == @"00\a\b\t\n\v\f\r00", "Failed to process string with disabled escapes."); + + //2. Double quotes - we use TryMatchDoubles which replaces single quotes with doubles and then calls TryMatch + token = parser.ParseInput(ReplaceQuotes(@"'00\a\b\t\n\v\f\r\'\\00' ")); + Assert.IsTrue((string)token.Value == "00\a\b\t\n\v\f\r\"\\00", "Failed to process escaped characters."); + token = parser.ParseInput(ReplaceQuotes("'abcd\nefg' ")); + Assert.IsTrue(token.IsError(), "Failed to detect erroneous multi-line string. (Double quotes)"); + token = parser.ParseInput(ReplaceQuotes("'''abcd\nefg''' ")); + Assert.IsTrue((string)token.Value == "abcd\nefg", "Failed to process line break in triple-quote string. (Double quotes)"); + token = parser.ParseInput(ReplaceQuotes(@"'''abcd\" + "\n" + "efg''' ")); + Assert.IsTrue((string)token.Value == "abcd\nefg", "Failed to process escaped line-break char. (Double quotes)"); + token = parser.ParseInput(ReplaceQuotes(@"r'00\a\b\t\n\v\f\r00' ")); + Assert.IsTrue((string)token.Value == @"00\a\b\t\n\v\f\r00", "Failed to process string with disabled escapes. (Double quotes)"); + }//method + + [TestMethod] + public void TestString_CSharp() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(TerminalFactory.CreateCSharpString("String")); + + token = parser.ParseInput('"' + @"abcd\\" + '"' + " "); + Assert.IsTrue((string)token.Value == @"abcd\", "Failed to process double escape char at the end of the string."); + + token = parser.ParseInput('"' + @"abcd\\\" + '"' + "efg" + '"' + " "); + Assert.IsTrue((string)token.Value == @"abcd\" + '"' + "efg" , @"Failed to process '\\\ + double-quote' inside the string."); + + //with Escapes + token = parser.ParseInput(ReplaceQuotes(@"'00\a\b\t\n\v\f\r\'\\00' ")); + Assert.IsTrue((string)token.Value == "00\a\b\t\n\v\f\r\"\\00", "Failed to process escaped characters."); + token = parser.ParseInput(ReplaceQuotes("'abcd\nefg' ")); + Assert.IsTrue(token.IsError(), "Failed to detect erroneous multi-line string."); + //with disabled escapes + token = parser.ParseInput(ReplaceQuotes(@"@'00\a\b\t\n\v\f\r00' ")); + Assert.IsTrue((string)token.Value == @"00\a\b\t\n\v\f\r00", "Failed to process @-string with disabled escapes."); + token = parser.ParseInput(ReplaceQuotes("@'abc\ndef' ")); + Assert.IsTrue((string)token.Value == "abc\ndef", "Failed to process @-string with linebreak."); + //Unicode and hex + token = parser.ParseInput(ReplaceQuotes(@"'abc\u0040def' ")); + Assert.IsTrue((string)token.Value == "abc@def", "Failed to process unicode escape \\u."); + token = parser.ParseInput(ReplaceQuotes(@"'abc\U00000040def' ")); + Assert.IsTrue((string)token.Value == "abc@def", "Failed to process unicode escape \\u."); + token = parser.ParseInput(ReplaceQuotes(@"'abc\x0040xyz' ")); + Assert.IsTrue((string)token.Value == "abc@xyz", "Failed to process hex escape (4 digits)."); + token = parser.ParseInput(ReplaceQuotes(@"'abc\x040xyz' ")); + Assert.IsTrue((string)token.Value == "abc@xyz", "Failed to process hex escape (3 digits)."); + token = parser.ParseInput(ReplaceQuotes(@"'abc\x40xyz' ")); + Assert.IsTrue((string)token.Value == "abc@xyz", "Failed to process hex escape (2 digits)."); + //octals + token = parser.ParseInput(ReplaceQuotes(@"'abc\0601xyz' ")); //the last digit "1" should not be included in octal number + Assert.IsTrue((string)token.Value == "abc01xyz", "Failed to process octal escape (3 + 1 digits)."); + token = parser.ParseInput(ReplaceQuotes(@"'abc\060xyz' ")); + Assert.IsTrue((string)token.Value == "abc0xyz", "Failed to process octal escape (3 digits)."); + token = parser.ParseInput(ReplaceQuotes(@"'abc\60xyz' ")); + Assert.IsTrue((string)token.Value == "abc0xyz", "Failed to process octal escape (2 digits)."); + token = parser.ParseInput(ReplaceQuotes(@"'abc\0xyz' ")); + Assert.IsTrue((string)token.Value == "abc\0xyz", "Failed to process octal escape (1 digit)."); + } + + [TestMethod] + public void TestString_CSharpChar() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(TerminalFactory.CreateCSharpChar("Char")); + token = parser.ParseInput("'a' "); + Assert.IsTrue((char)token.Value == 'a', "Failed to process char."); + token = parser.ParseInput(@"'\n' "); + Assert.IsTrue((char)token.Value == '\n', "Failed to process new-line char."); + token = parser.ParseInput(@"'' "); + Assert.IsTrue(token.IsError(), "Failed to recognize empty quotes as invalid char literal."); + token = parser.ParseInput(@"'abc' "); + Assert.IsTrue(token.IsError(), "Failed to recognize multi-char sequence as invalid char literal."); + //Note: unlike strings, c# char literals don't allow the "@" prefix + } + + [TestMethod] + public void TestString_VB() { + Parser parser; Token token; + + parser = TestHelper.CreateParser(TerminalFactory.CreateVbString("String")); + //VB has no escapes - so make sure term doesn't catch any escapes + token = parser.ParseInput(ReplaceQuotes(@"'00\a\b\t\n\v\f\r\\00' ")); + Assert.IsTrue((string)token.Value == @"00\a\b\t\n\v\f\r\\00", "Failed to process string with \\ characters."); + token = parser.ParseInput(ReplaceQuotes("'abcd\nefg' ")); + Assert.IsTrue(token.IsError(), "Failed to detect erroneous multi-line string."); + token = parser.ParseInput(ReplaceQuotes("'abcd''efg' ")); + Assert.IsTrue((string)token.Value == "abcd\"efg", "Failed to process a string with doubled double-quote char."); + //Test char suffix "c" + token = parser.ParseInput(ReplaceQuotes("'A'c ")); + Assert.IsTrue((char)token.Value == 'A', "Failed to process a character"); + token = parser.ParseInput(ReplaceQuotes("''c ")); + Assert.IsTrue(token.IsError(), "Failed to detect an error for an empty char."); + token = parser.ParseInput(ReplaceQuotes("'ab'C ")); + Assert.IsTrue(token.IsError(), "Failed to detect error in multi-char sequence."); + } + + }//class +}//namespace diff --git a/Irony.Tests/TestHelper.cs b/Irony.Tests/TestHelper.cs new file mode 100644 index 0000000..780a0db --- /dev/null +++ b/Irony.Tests/TestHelper.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Parsing; + +namespace Irony.Tests { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + public static class TestHelper { + //A skeleton for a grammar with a single terminal, followed by optional terminator + class TerminalTestGrammar : Grammar { + public string Terminator; + public TerminalTestGrammar(Terminal terminal, string terminator = null) : base(caseSensitive: true) { + Terminator = terminator; + var rule = new BnfExpression(terminal); + if (Terminator != null) { + MarkReservedWords(Terminator); + rule += Terminator; + } + base.Root = new NonTerminal("Root"); + Root.Rule = rule; + } + + }//class + + public static Parser CreateParser(Terminal terminal, string terminator = "end") { + var grammar = new TerminalTestGrammar(terminal, terminator); + var parser = new Parser(grammar); + CheckGrammarErrors(parser); + return parser; + } + + public static void CheckGrammarErrors(Parser parser) { + var errors = parser.Language.Errors; + if (errors.Count > 0) + throw new Exception("Unexpected grammar contains error(s): " + string.Join("\n", errors)); + } + public static void CheckParseErrors(ParseTree parseTree) { + if (parseTree.HasErrors()) + throw new Exception("Unexpected parse error(s): " + string.Join("\n", parseTree.ParserMessages)); + } + + public static Token ParseInput(this Parser parser, string input, bool useTerminator = true) { + var g = (TerminalTestGrammar) parser.Language.Grammar; + useTerminator &= g.Terminator != null; + if (useTerminator) + input += " " + g.Terminator; + var tree = parser.Parse(input); + //If error, then return this error token, this is probably what is expected. + var first = tree.Tokens[0]; + if (first.IsError()) + return first; + //Verify that last or before-last token is a terminator + if (useTerminator) { + Assert.IsTrue(tree.Tokens.Count >= 2, "Wrong # of tokens - expected at least 2. Input: " + input); + var count = tree.Tokens.Count; + //The last is EOF, the one before last should be a terminator + Assert.AreEqual(g.Terminator, tree.Tokens[count - 2].Text, "Input terminator not found in the second token. Input: " + input); + } + return tree.Tokens[0]; + } + + }//class +} diff --git a/Irony.Tests/TokenPreviewResolution/ConflictGrammars.cs b/Irony.Tests/TokenPreviewResolution/ConflictGrammars.cs new file mode 100644 index 0000000..b08c2af --- /dev/null +++ b/Irony.Tests/TokenPreviewResolution/ConflictGrammars.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Irony.Parsing; + +namespace Irony.Tests.TokenPreviewResolution { + + [Language("Grammar with conflicts, no hints", "1.1", "Grammar with conflicts, no hints.")] + public class ConflictGrammarNoHints : Grammar { + public ConflictGrammarNoHints() + : base(true) { + var name = new IdentifierTerminal("id"); + + var stmt = new NonTerminal("Statement"); + var stmtList = new NonTerminal("StatementList"); + var fieldModifier = new NonTerminal("fieldModifier"); + var propModifier = new NonTerminal("propModifier"); + var methodModifier = new NonTerminal("methodModifier"); + var fieldModifierList = new NonTerminal("fieldModifierList"); + var propModifierList = new NonTerminal("propModifierList"); + var methodModifierList = new NonTerminal("methodModifierList"); + var fieldDef = new NonTerminal("fieldDef"); + var propDef = new NonTerminal("propDef"); + var methodDef = new NonTerminal("methodDef"); + + //Rules + this.Root = stmtList; + stmtList.Rule = MakePlusRule(stmtList, stmt); + stmt.Rule = fieldDef | propDef | methodDef; + fieldDef.Rule = fieldModifierList + name + name + ";"; + propDef.Rule = propModifierList + name + name + "{" + "}"; + methodDef.Rule = methodModifierList + name + name + "(" + ")" + "{" + "}"; + fieldModifierList.Rule = MakeStarRule(fieldModifierList, fieldModifier); + propModifierList.Rule = MakeStarRule(propModifierList, propModifier); + methodModifierList.Rule = MakeStarRule(methodModifierList, methodModifier); + + // That's the key of the problem: 3 modifiers have common members + // so parser automaton has hard time deciding which modifiers list to produce - + // is it a field, prop or method we are beginning to parse? + fieldModifier.Rule = ToTerm("public") | "private" | "readonly" | "volatile"; + propModifier.Rule = ToTerm("public") | "private" | "readonly" | "override"; + methodModifier.Rule = ToTerm("public") | "private" | "override"; + + MarkReservedWords("public", "private", "readonly", "volatile", "override"); + } + } + + [Language("Grammar with conflicts #2", "1.1", "Conflict grammar with hints added to productions.")] + public class ConflictGrammarWithHintsInRules : Grammar { + public ConflictGrammarWithHintsInRules() : base(true) { + var name = new IdentifierTerminal("id"); + + var definition = new NonTerminal("definition"); + var fieldDef = new NonTerminal("fieldDef"); + var propDef = new NonTerminal("propDef"); + var fieldModifier = new NonTerminal("fieldModifier"); + var propModifier = new NonTerminal("propModifier"); + + definition.Rule = fieldDef | propDef; + fieldDef.Rule = fieldModifier + name + name + ";"; + propDef.Rule = propModifier + name + name + "{" + "}"; + var fieldHint = ReduceIf(";", comesBefore: "{"); + fieldModifier.Rule = "public" + fieldHint | "private" + fieldHint | "readonly"; + propModifier.Rule = ToTerm("public") | "private" | "override"; + + Root = definition; + } + }//class + + [Language("Grammar with conflicts #4", "1.1", "Test conflict grammar with conflicts and hints: hints are added to non-terminals.")] + public class ConflictGrammarWithHintsOnTerms : Grammar { + public ConflictGrammarWithHintsOnTerms() + : base(true) { + var name = new IdentifierTerminal("id"); + + var stmt = new NonTerminal("Statement"); + var stmtList = new NonTerminal("StatementList"); + var fieldModifier = new NonTerminal("fieldModifier"); + var propModifier = new NonTerminal("propModifier"); + var methodModifier = new NonTerminal("methodModifier"); + var fieldModifierList = new NonTerminal("fieldModifierList"); + var propModifierList = new NonTerminal("propModifierList"); + var methodModifierList = new NonTerminal("methodModifierList"); + var fieldDef = new NonTerminal("fieldDef"); + var propDef = new NonTerminal("propDef"); + var methodDef = new NonTerminal("methodDef"); + + //Rules + this.Root = stmtList; + stmtList.Rule = MakePlusRule(stmtList, stmt); + stmt.Rule = fieldDef | propDef | methodDef; + fieldDef.Rule = fieldModifierList + name + name + ";"; + propDef.Rule = propModifierList + name + name + "{" + "}"; + methodDef.Rule = methodModifierList + name + name + "(" + ")" + "{" + "}"; + fieldModifierList.Rule = MakeStarRule(fieldModifierList, fieldModifier); + propModifierList.Rule = MakeStarRule(propModifierList, propModifier); + methodModifierList.Rule = MakeStarRule(methodModifierList, methodModifier); + + fieldModifier.Rule = ToTerm("public") | "private" | "readonly" | "volatile"; + propModifier.Rule = ToTerm("public") | "private" | "readonly" | "override"; + methodModifier.Rule = ToTerm("public") | "private" | "override"; + + // conflict resolution + var fieldHint = new TokenPreviewHint(PreferredActionType.Reduce, thisSymbol: ";", comesBefore: new string[] { "(", "{" }); + fieldModifier.AddHintToAll(fieldHint); + fieldModifierList.AddHintToAll(fieldHint); + var propHint = new TokenPreviewHint(PreferredActionType.Reduce, thisSymbol: "{", comesBefore: new string[] { ";", "(" }); + propModifier.AddHintToAll(propHint); + propModifierList.AddHintToAll(propHint); + + MarkReservedWords("public", "private", "readonly", "volatile", "override"); + } + } + + +} diff --git a/Irony.Tests/TokenPreviewResolution/ConflictResolutionTests.cs b/Irony.Tests/TokenPreviewResolution/ConflictResolutionTests.cs new file mode 100644 index 0000000..e73cbab --- /dev/null +++ b/Irony.Tests/TokenPreviewResolution/ConflictResolutionTests.cs @@ -0,0 +1,124 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; + +using Irony.Parsing; + +namespace Irony.Tests.TokenPreviewResolution { +#if USE_NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestInitialize = NUnit.Framework.SetUpAttribute; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + + [TestClass] + public class ConflictResolutionTests { + + // samples to be parsed + const string FieldSample = "private int SomeField;"; + const string PropertySample = "public string Name {}"; + const string FieldListSample = "private int Field1; public string Field2;"; + const string MixedListSample = @" + public int Size {} + private string TableName; + override void Run() + { + }"; + + // Full grammar, no hints - expect errors --------------------------------------------------------------------- + [TestMethod] + public void TestConflictGrammarNoHints_HasErrors() { + var grammar = new ConflictGrammarNoHints(); + var parser = new Parser(grammar); + Assert.IsTrue(parser.Language.Errors.Count > 0); + //Cannot parse mixed list + var sample = MixedListSample; + var tree = parser.Parse(sample); + Assert.IsNotNull(tree); + Assert.IsTrue(tree.HasErrors()); + } + + // Hints in Rules -------------------------------------------------------------------------- + [TestMethod] + public void TestConflictGrammarWithHintsOnRules() { + var grammar = new ConflictGrammarWithHintsInRules(); + var parser = new Parser(grammar); + Assert.IsTrue(parser.Language.Errors.Count == 0); + // Field sample + var sample = FieldSample; + var tree = parser.Parse(sample); + Assert.IsNotNull(tree); + Assert.IsFalse(tree.HasErrors()); + + Assert.IsNotNull(tree.Root); + var term = tree.Root.Term as NonTerminal; + Assert.IsNotNull(term); + Assert.AreEqual("definition", term.Name); + + Assert.AreEqual(1, tree.Root.ChildNodes.Count); + var modNode = tree.Root.ChildNodes[0].ChildNodes[0]; + Assert.AreEqual("fieldModifier", modNode.Term.Name); + + //Property + sample = PropertySample; + tree = parser.Parse(sample); + Assert.IsNotNull(tree); + Assert.IsFalse(tree.HasErrors()); + + Assert.IsNotNull(tree.Root); + term = tree.Root.Term as NonTerminal; + Assert.IsNotNull(term); + Assert.AreEqual("definition", term.Name); + + Assert.AreEqual(1, tree.Root.ChildNodes.Count); + modNode = tree.Root.ChildNodes[0].ChildNodes[0]; + Assert.AreEqual("propModifier", modNode.Term.Name); + } + + //Hints on terms --------------------------------------------------------------------- + [TestMethod] + public void TestConflictGrammar_HintsOnTerms() { + var grammar = new ConflictGrammarWithHintsOnTerms(); + var parser = new Parser(grammar); + Assert.IsTrue(parser.Language.Errors.Count == 0); + + //Field list sample + var sample = FieldListSample; + var tree = parser.Parse(sample); + Assert.IsNotNull(tree); + Assert.IsFalse(tree.HasErrors()); + + Assert.IsNotNull(tree.Root); + var term = tree.Root.Term as NonTerminal; + Assert.IsNotNull(term); + Assert.AreEqual("StatementList", term.Name); + + Assert.AreEqual(2, tree.Root.ChildNodes.Count); + var nodes = tree.Root.ChildNodes.Select(t => t.ChildNodes[0]).ToArray(); + Assert.AreEqual("fieldDef", nodes[0].Term.Name); + Assert.AreEqual("fieldDef", nodes[1].Term.Name); + + //Mixed sample + sample = MixedListSample; + tree = parser.Parse(sample); + Assert.IsNotNull(tree); + Assert.IsFalse(tree.HasErrors()); + + Assert.IsNotNull(tree.Root); + term = tree.Root.Term as NonTerminal; + Assert.IsNotNull(term); + Assert.AreEqual("StatementList", term.Name); + + Assert.AreEqual(3, tree.Root.ChildNodes.Count); + nodes = tree.Root.ChildNodes.Select(t => t.ChildNodes[0]).ToArray(); + Assert.AreEqual("propDef", nodes[0].Term.Name); + Assert.AreEqual("fieldDef", nodes[1].Term.Name); + Assert.AreEqual("methodDef", nodes[2].Term.Name); + } + + } +} diff --git a/Irony.Tests/_ Tests - read me.txt b/Irony.Tests/_ Tests - read me.txt new file mode 100644 index 0000000..6a4ad25 --- /dev/null +++ b/Irony.Tests/_ Tests - read me.txt @@ -0,0 +1,17 @@ +Running Irony unit tests. +Irony unit tests had been adjusted to run both Visual Studio test system and NUnit framework. +There are two test projects in the solution, one for each environment. They share all test definitions (*.cs files). +Adjustment in code is done through redefinition of test-related attributes for NUnit in each file in conditional compilation clause. +NUnit project is set to "no-compile" in default (Debug) configuration - it references NUnit assemblies which +might not be there if you don't use NUnit, but this won't prevent you from compiling the solution. + +VS Test project should be run directly in Visual Studio (version with Testing framework). Set the VsTest project as startup and +click Run - all tests will be executed. + +For NUnit project, you must first add (refresh) reference to NUnit.Framework.dll. (You should download the package from NUnit site) +After refreshing the reference, compile the project. Then you need to update the startup app - running this project +should automatically start NUnit GUI App. Right click on NUnit project in solution explorer, select properties; switch to Debug page, +select "Start External Program" for Start action, and enter/lookup the path to the nunit.exe application (it is NUnit GUI). +Run the project. Once the NUnit GUI appears, locate the Irony.Tests.NUnit.dll assembly in bin\debug folder, and then drap and drop +it into the left pane of Nunit GUI. You will see the tree of tests expanded in the pane. Click Run button - the tests should run. +(You have to do this drag-drop only once: next time you run NUnit it will load the assembly automatically) \ No newline at end of file diff --git a/Irony/010.Irony.2010.csproj b/Irony/010.Irony.2010.csproj new file mode 100644 index 0000000..bb644ab --- /dev/null +++ b/Irony/010.Irony.2010.csproj @@ -0,0 +1,203 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + Library + Properties + Irony + Irony + + + 3.5 + + + v4.0 + + + + + + + + + true + irony.snk + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + false + + + pdbonly + true + bin\Release\ + + + prompt + 4 + AllRules.ruleset + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + PublicResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/Irony/101.IronySilverlight.2010.csproj b/Irony/101.IronySilverlight.2010.csproj new file mode 100644 index 0000000..d3757c2 --- /dev/null +++ b/Irony/101.IronySilverlight.2010.csproj @@ -0,0 +1,167 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {1CBAD18E-5805-428C-9E76-EFEDBFC7C8A9} + {A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Irony + IronySilverlight + Silverlight + v4.0 + $(TargetFrameworkVersion) + false + true + true + + + + v3.5 + + + true + full + false + Bin\Debug + DEBUG;TRACE;SILVERLIGHT + true + true + prompt + 4 + + + pdbonly + true + Bin\Release + TRACE;SILVERLIGHT + true + true + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Irony/Ast/AstBuilder.cs b/Irony/Ast/AstBuilder.cs new file mode 100644 index 0000000..377677b --- /dev/null +++ b/Irony/Ast/AstBuilder.cs @@ -0,0 +1,135 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using Irony.Parsing; +using System.Reflection.Emit; + +namespace Irony.Ast { + + public class AstBuilder { + public AstContext Context; + + public AstBuilder(AstContext context) { + Context = context; + } + + public virtual void BuildAst(ParseTree parseTree) { + if (parseTree.Root == null) + return; + Context.Messages = parseTree.ParserMessages; + if (!Context.Language.AstDataVerified) + VerifyLanguageData(); + if (Context.Language.ErrorLevel == GrammarErrorLevel.Error) + return; + BuildAst(parseTree.Root); + } + + public virtual void VerifyLanguageData() { + var gd = Context.Language.GrammarData; + //Collect all terminals and non-terminals + var terms = new BnfTermSet(); + //SL does not understand co/contravariance, so doing merge one-by-one + foreach (var t in gd.Terminals) terms.Add(t); + foreach (var t in gd.NonTerminals) terms.Add(t); + var missingList = new BnfTermList(); + foreach (var term in terms) { + var terminal = term as Terminal; + if (terminal != null && terminal.Category != TokenCategory.Content) continue; //only content terminals + if (term.Flags.IsSet(TermFlags.NoAstNode)) continue; + var config = term.AstConfig; + if (config.NodeCreator != null || config.DefaultNodeCreator != null) continue; + //We must check NodeType + if (config.NodeType == null) + config.NodeType = GetDefaultNodeType(term); + if (config.NodeType == null) + missingList.Add(term); + else + config.DefaultNodeCreator = CompileDefaultNodeCreator(config.NodeType); + } + if (missingList.Count > 0) + Context.AddMessage(ErrorLevel.Error, SourceLocation.Empty, Resources.ErrNodeTypeNotSetOn, missingList.ToString()); + Context.Language.AstDataVerified = true; + } + // AST node type is not specified for term {0}. Either assign Term.AstConfig.NodeType, or specify default type(s) in AstBuilder. + protected virtual Type GetDefaultNodeType(BnfTerm term) { + if (term is NumberLiteral || term is StringLiteral) + return Context.DefaultLiteralNodeType; + else if (term is IdentifierTerminal) + return Context.DefaultIdentifierNodeType; + else + return Context.DefaultNodeType; + } + + public virtual void BuildAst(ParseTreeNode parseNode) { + var term = parseNode.Term; + if (term.Flags.IsSet(TermFlags.NoAstNode) || parseNode.AstNode != null) return; + //children first + var processChildren = !parseNode.Term.Flags.IsSet(TermFlags.AstDelayChildren) && parseNode.ChildNodes.Count > 0; + if (processChildren) { + var mappedChildNodes = parseNode.GetMappedChildNodes(); + for (int i = 0; i < mappedChildNodes.Count; i++) + BuildAst(mappedChildNodes[i]); + } + //create the node + //We know that either NodeCreator or DefaultNodeCreator is set; VerifyAstData create the DefaultNodeCreator + var config = term.AstConfig; + if (config.NodeCreator != null) { + config.NodeCreator(Context, parseNode); + // We assume that Node creator method creates node and initializes it, so parser does not need to call + // IAstNodeInit.Init() method on node object. But we do call AstNodeCreated custom event on term. + } else { + //Invoke the default creator compiled when we verified the data + parseNode.AstNode = config.DefaultNodeCreator(); + //Initialize node + var iInit = parseNode.AstNode as IAstNodeInit; + if (iInit != null) + iInit.Init(Context, parseNode); + } + //Invoke the event on term + term.OnAstNodeCreated(parseNode); + }//method + + //Contributed by William Horner (wmh) + private DefaultAstNodeCreator CompileDefaultNodeCreator(Type nodeType) { + ConstructorInfo constr = nodeType.GetConstructor(Type.EmptyTypes); + DynamicMethod method = new DynamicMethod("CreateAstNode", nodeType, Type.EmptyTypes); + ILGenerator il = method.GetILGenerator(); + il.Emit(OpCodes.Newobj, constr); + il.Emit(OpCodes.Ret); + var result = (DefaultAstNodeCreator) method.CreateDelegate(typeof(DefaultAstNodeCreator)); + return result; + } + +/* + //A list of of child nodes based on AstPartsMap. By default, the same as ChildNodes + private ParseTreeNodeList _mappedChildNodes; + public ParseTreeNodeList MappedChildNodes { + get { + if (_mappedChildNodes == null) + _mappedChildNodes = GetMappedChildNodes(); + return _mappedChildNodes; + } + } +*/ + + + + + }//class + + +} diff --git a/Irony/Ast/AstContext.cs b/Irony/Ast/AstContext.cs new file mode 100644 index 0000000..cc73536 --- /dev/null +++ b/Irony/Ast/AstContext.cs @@ -0,0 +1,40 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Ast { + public class AstContext { + public readonly LanguageData Language; + public Type DefaultNodeType; + public Type DefaultLiteralNodeType; //default node type for literals + public Type DefaultIdentifierNodeType; //default node type for identifiers + + public Dictionary Values = new Dictionary(); + public LogMessageList Messages; + + public AstContext(LanguageData language) { + Language = language; + } + + public void AddMessage(ErrorLevel level, SourceLocation location, string message, params object[] args) { + if (args != null && args.Length > 0) + message = string.Format(message, args); + Messages.Add(new LogMessage(level, location, message, null)); + } + + }//class +}//ns diff --git a/Irony/Ast/AstExtensions.cs b/Irony/Ast/AstExtensions.cs new file mode 100644 index 0000000..3d1d2ec --- /dev/null +++ b/Irony/Ast/AstExtensions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Parsing; + +namespace Irony.Ast { + public static class AstExtensions { + + public static ParseTreeNodeList GetMappedChildNodes(this ParseTreeNode node) { + var term = node.Term; + if (!term.HasAstConfig()) + return node.ChildNodes; + var map = term.AstConfig.PartsMap; + //If no map then mapped list is the same as original + if (map == null) return node.ChildNodes; + //Create mapped list + var result = new ParseTreeNodeList(); + for (int i = 0; i < map.Length; i++) { + var index = map[i]; + result.Add(node.ChildNodes[index]); + } + return result; + } + + + } +} diff --git a/Irony/Ast/AstInterfaces.cs b/Irony/Ast/AstInterfaces.cs new file mode 100644 index 0000000..8a8ed6a --- /dev/null +++ b/Irony/Ast/AstInterfaces.cs @@ -0,0 +1,44 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections; +using System.Linq; +using System.Text; +using Irony.Parsing; + +namespace Irony.Ast { + // Grammar Explorer uses this interface to discover and display the AST tree after parsing the input + // (Grammar Explorer additionally uses ToString method of the node to get the text representation of the node) + public interface IBrowsableAstNode { + int Position { get; } + IEnumerable GetChildNodes(); + } + + // Note that we expect more than one interpreter/AST implementation. + // Irony.Interpreter namespace provides just one of them. That's why the following AST interfaces + // are here, in top Irony namespace and not in Irony.Interpreter.Ast. + // In the future, I plan to introduce advanced interpreter, with its own set of AST classes - it will live + // in a separate assembly Irony.Interpreter2.dll. + + // Basic interface for AST nodes; Init method is the chance for AST node to get references to its child nodes, and all + // related information gathered during parsing + // Implementing this interface is a minimum required from custom AST node class to enable its creation by Irony AST builder + // Alternatively, if your custom AST node class does not implement this interface then you can create + // and initialize node instances using AstNodeCreator delegate attached to corresponding non-terminal in your grammar. + public interface IAstNodeInit { + void Init(AstContext context, ParseTreeNode parseNode); + } + + + +} diff --git a/Irony/Ast/AstNodeConfig.cs b/Irony/Ast/AstNodeConfig.cs new file mode 100644 index 0000000..e0c3713 --- /dev/null +++ b/Irony/Ast/AstNodeConfig.cs @@ -0,0 +1,59 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Parsing; + +namespace Irony.Ast { + + public class AstNodeEventArgs : EventArgs { + public AstNodeEventArgs(ParseTreeNode parseTreeNode) { + ParseTreeNode = parseTreeNode; + } + public readonly ParseTreeNode ParseTreeNode; + public object AstNode { + get { return ParseTreeNode.AstNode; } + } + } + + public delegate void AstNodeCreator(AstContext context, ParseTreeNode parseNode); + public delegate object DefaultAstNodeCreator(); + + public class AstNodeConfig { + + public Type NodeType; + public object Data; //config data passed to AstNode + public AstNodeCreator NodeCreator; // a custom method for creating AST nodes + public DefaultAstNodeCreator DefaultNodeCreator; //default method for creating AST nodes; compiled dynamic method, wrapper around "new nodeType();" + + // An optional map (selector, filter) of child AST nodes. This facility provides a way to adjust the "map" of child nodes in various languages to + // the structure of a standard AST nodes (that can be shared betweeen languages). + // ParseTreeNode object has two properties containing list nodes: ChildNodes and MappedChildNodes. + // If term.AstPartsMap is null, these two child node lists are identical and contain all child nodes. + // If AstParts is not null, then MappedChildNodes will contain child nodes identified by indexes in the map. + // For example, if we set + // term.AstPartsMap = new int[] {1, 4, 2}; + // then MappedChildNodes will contain 3 child nodes, which are under indexes 1, 4, 2 in ChildNodes list. + // The mapping is performed in CoreParser.cs, method CheckCreateMappedChildNodeList. + public int[] PartsMap; + + + public bool CanCreateNode() { + return NodeCreator != null || NodeType != null; + } + + }//AstNodeConfig class +} diff --git a/Irony/MS-PubLicense.Rtf b/Irony/MS-PubLicense.Rtf new file mode 100644 index 0000000..9cadaea --- /dev/null +++ b/Irony/MS-PubLicense.Rtf @@ -0,0 +1,177 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f34\fbidi \froman\fcharset1\fprq2{\*\panose 02040503050406030204}Cambria Math;} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f39\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\f40\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f42\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f43\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f44\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f45\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f46\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f47\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;}{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;} +{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;}{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;} +{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} +{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} +{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; +\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 +\ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 +\snext11 \ssemihidden \sunhideused \sqformat Normal Table;}}{\*\rsidtbl \rsid11612883}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author dinov}{\operator dinov} +{\creatim\yr2007\mo10\dy30\hr14\min43}{\revtim\yr2007\mo10\dy30\hr14\min43}{\version2}{\edmins1}{\nofpages2}{\nofwords404}{\nofchars2212}{\*\company Microsoft}{\nofcharsws2611}{\vern32893}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/200 +3/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves1\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 +\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot11612883 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 +\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sb100\sa100\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 +\fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab\af0\afs28 \ltrch\fcs0 \b\f0\fs28\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 Microsoft }{\rtlch\fcs1 \ab\af0\afs28 \ltrch\fcs0 +\b\f0\fs28\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 Public }{\rtlch\fcs1 \ab\af0\afs28 \ltrch\fcs0 \b\f0\fs28\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 License (Ms-PL) +\par }{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid11612883 +\par }{\rtlch\fcs1 \ab\af0\afs36 \ltrch\fcs0 \b\f0\fs36\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 1. Definitions +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 \hich\f0 The terms \'93\loch\f0 \hich\f0 reproduce,\'94\loch\f0 \hich\f0 \'93\loch\f0 \hich\f0 reproduction,\'94\loch\f0 \hich\f0 \'93 +\hich\af0\dbch\af31505\loch\f0 \hich\f0 derivative works,\'94\loch\f0 \hich\f0 and \'93\loch\f0 \hich\f0 distribution\'94\loch\f0 have the same meaning here as under U.S. copyright law. +\par \hich\af0\dbch\af31505\loch\f0 \hich\f0 A \'93\loch\f0 \hich\f0 contribution\'94\loch\f0 is the original software, or any additions or changes to the software. +\par \hich\af0\dbch\af31505\loch\f0 \hich\f0 A \'93\loch\f0 \hich\f0 contributor\'94\loch\f0 is any person that distributes its contribution under this\hich\af0\dbch\af31505\loch\f0 license. +\par \loch\af0\dbch\af31505\hich\f0 \'93\loch\f0 \hich\f0 Licensed patents\'94\loch\f0 are a contributor\hich\f0 \rquote \loch\f0 s patent claims that read directly on its contribution. +\par }{\rtlch\fcs1 \ab\af0\afs36 \ltrch\fcs0 \b\f0\fs36\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 2. Grant of Rights +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contrib +\hich\af0\dbch\af31505\loch\f0 utor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + +\par \hich\af0\dbch\af31505\loch\f0 (B) Patent Grant- Subject to th\hich\af0\dbch\af31505\loch\f0 +e terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or o +\hich\af0\dbch\af31505\loch\f0 t\hich\af0\dbch\af31505\loch\f0 herwise dispose of its contribution in the software or derivative works of the contribution in the software. +\par }{\rtlch\fcs1 \ab\af0\afs36 \ltrch\fcs0 \b\f0\fs36\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 3. Conditions and Limitations +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid11612883 \hich\af0\dbch\af31505\loch\f0 (A) No Trademark License- This license does not grant you rights to use any contributors\hich\f0 \rquote \loch\f0 name, logo, or trademarks. +\par \hich\af0\dbch\af31505\loch\f0 (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. +\par \hich\af0\dbch\af31505\loch\f0 (C) If you distribute any portion of the software, you must ret\hich\af0\dbch\af31505\loch\f0 ain all copyright, patent, trademark, and attribution notices that are present in the software. +\par \hich\af0\dbch\af31505\loch\f0 (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with y\hich\af0\dbch\af31505\loch\f0 +our distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. +\par \hich\af0\dbch\af31505\loch\f0 \hich\f0 (E) The software is licensed \'93\loch\f0 \hich\f0 as-is.\'94\loch\f0 You bear the risk of using it. The contributors give \hich\af0\dbch\af31505\loch\f0 +no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantabil +\hich\af0\dbch\af31505\loch\f0 i\hich\af0\dbch\af31505\loch\f0 ty, fitness for a particular purpose and non-infringement. +\par }\pard \ltrpar\ql \li0\ri0\nowidctlpar\wrapdefault\faauto\rin0\lin0\itap0 {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid11612883 +\par }{\*\themedata 504b030414000600080000002100828abc13fa0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb6ac3301045f785fe83d0b6d8 +72ba28a5d8cea249777d2cd20f18e4b12d6a8f843409c9df77ecb850ba082d74231062ce997b55ae8fe3a00e1893f354e9555e6885647de3a8abf4fbee29bbd7 +2a3150038327acf409935ed7d757e5ee14302999a654e99e393c18936c8f23a4dc072479697d1c81e51a3b13c07e4087e6b628ee8cf5c4489cf1c4d075f92a0b +44d7a07a83c82f308ac7b0a0f0fbf90c2480980b58abc733615aa2d210c2e02cb04430076a7ee833dfb6ce62e3ed7e14693e8317d8cd0433bf5c60f53fea2fe7 +065bd80facb647e9e25c7fc421fd2ddb526b2e9373fed4bb902e182e97b7b461e6bfad3f010000ffff0300504b030414000600080000002100a5d6a7e7c00000 +00360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4fc7060abb08 +84a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b63095120f88d94fbc +52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462a1a82fe353 +bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f7468656d652f7468 +656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b4b0d592c9c +070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b4757e8d3f7 +29e245eb2b260a0238fd010000ffff0300504b03041400060008000000210096b5ade296060000501b0000160000007468656d652f7468656d652f7468656d65 +312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87615b8116d8 +a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad79482a9c04 +98f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b5d8a314d3c +94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab999fb7b471 +7509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9699640f671 +9e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd5868b37a088d1 +e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d60cf03ac1a5 +193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f9e7ef3f2d1 +17d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be15c308d3f2 +8acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a99793849c26ae6 +6252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d32a423279a +668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2af074481847 +bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86e877f0034e +16bafb0e258ebb4faf06b769e888340b103d3311da9750aa9d0a1cd3e4efca31a3508f6d0c5c5c398602f8e2ebc71591f5b616e24dd893aa3261fb44f95d843b +5974bb5c04f4edafb95b7892ec1108f3f98de75dc97d5772bdff7cc95d94cf672db4b3da0a6557f70db629362d72bcb0431e53c6066acac80d699a6409fb44d0 +8741bdce9c0e4971624a2378cceaba830b05366b90e0ea23aaa241845368b0eb9e2612ca8c742851ca251ceccc70256d8d87265dd96361531f186c3d9058edf2 +c00eafe8e1fc5c509031bb4d680e9f39a3154de0accc56ae644441edd76156d7429d995bdd88664a9dc3ad50197c38af1a0c16d684060441db02565e85f3b966 +0d0713cc48a0ed6ef7dedc2dc60b17e92219e180643ed27acffba86e9c94c78ab90980d8a9f0913ee49d62b512b79626fb06dccee2a432bbc60276b9f7dec44b +7904cfbca4f3f6443ab2a49c9c2c41476dafd55c6e7ac8c769db1bc399161ee314bc2e75cf8759081743be1236ec4f4d6693e5336fb672c5dc24a8c33585b5fb +9cc24e1d4885545b58463634cc5416022cd19cacfccb4d30eb45296023fd35a458598360f8d7a4003bbaae25e331f155d9d9a5116d3bfb9a95523e51440ca2e0 +088dd844ec6370bf0e55d027a012ae264c45d02f708fa6ad6da6dce29c255df9f6cae0ec38666984b372ab5334cf640b37795cc860de4ae2816e95b21be5ceaf +8a49f90b52a51cc6ff3355f47e0237052b81f6800fd7b802239daf6d8f0b1571a8426944fdbe80c6c1d40e8816b88b8569082ab84c36ff0539d4ff6dce591a26 +ade1c0a7f669880485fd484582903d284b26fa4e2156cff62e4b9265844c4495c495a9157b440e091bea1ab8aaf7760f4510eaa69a6465c0e04ec69ffb9e65d0 +28d44d4e39df9c1a52ecbd3607fee9cec7263328e5d661d3d0e4f62f44acd855ed7ab33cdf7bcb8ae889599bd5c8b3029895b6825696f6af29c239b75a5bb1e6 +345e6ee6c28117e73586c1a2214ae1be07e93fb0ff51e133fb65426fa843be0fb515c187064d0cc206a2fa926d3c902e907670048d931db4c1a44959d366ad93 +b65abe595f70a75bf03d616c2dd959fc7d4e6317cd99cbcec9c58b34766661c7d6766ca1a9c1b327531486c6f941c638c67cd22a7f75e2a37be0e82db8df9f30 +254d30c1372581a1f51c983c80e4b71ccdd28dbf000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f74 +68656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f24 +51eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198 +720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528 +a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100828abc13fa0000001c0200001300000000000000000000000000 +000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b000000000000000000000000 +002b0100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000140200007468 +656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210096b5ade296060000501b000016000000000000000000 +00000000d10200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b010000270000000000 +00000000000000009b0900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000960a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e352e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffffec69d9888b8b3d4c859eaf6cd158be0f0000000000000000000000009055 +58f93d1bc801feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/Irony/Parsing/Data/Construction/GrammarDataBuilder.cs b/Irony/Parsing/Data/Construction/GrammarDataBuilder.cs new file mode 100644 index 0000000..85b77fa --- /dev/null +++ b/Irony/Parsing/Data/Construction/GrammarDataBuilder.cs @@ -0,0 +1,276 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing.Construction { + + internal class GrammarDataBuilder { + LanguageData _language; + Grammar _grammar; + GrammarData _grammarData; + int _unnamedCount; //internal counter for generating names for unnamed non-terminals + internal int _lastItemId; //each LR0Item gets its unique ID, last assigned (max) Id is kept in this field + + internal GrammarDataBuilder(LanguageData language) { + _language = language; + _grammar = _language.Grammar; + } + + internal void Build() { + _grammarData = _language.GrammarData; + CreateAugmentedRoots(); + CollectTermsFromGrammar(); + InitTermLists(); + FillOperatorReportGroup(); + CreateProductions(); + ComputeNonTerminalsNullability(_grammarData); + ComputeTailsNullability(_grammarData); + ValidateGrammar(); + } + + private void CreateAugmentedRoots() { + _grammarData.AugmentedRoot = CreateAugmentedRoot(_grammar.Root); + foreach(var snippetRoot in _grammar.SnippetRoots) + _grammarData.AugmentedSnippetRoots.Add(CreateAugmentedRoot(snippetRoot)); + } + + private NonTerminal CreateAugmentedRoot(NonTerminal root) { + var result = new NonTerminal(root.Name + "'", root + _grammar.Eof); + result.SetFlag(TermFlags.NoAstNode); //mark that we don't need AST node here + return result; + } + + private void CollectTermsFromGrammar() { + _unnamedCount = 0; + _grammarData.AllTerms.Clear(); + //Start with NonGrammarTerminals, and set IsNonGrammar flag + foreach (Terminal t in _grammarData.Grammar.NonGrammarTerminals) { + t.SetFlag(TermFlags.IsNonGrammar); + _grammarData.AllTerms.Add(t); + } + //Add main root + CollectTermsRecursive(_grammarData.AugmentedRoot); + foreach(var augmRoot in _grammarData.AugmentedSnippetRoots) + CollectTermsRecursive(augmRoot); + //Add syntax error explicitly + _grammarData.AllTerms.Add(_grammar.SyntaxError); + } + + private void CollectTermsRecursive(BnfTerm term) { + if (_grammarData.AllTerms.Contains(term)) return; + _grammarData.AllTerms.Add(term); + NonTerminal nt = term as NonTerminal; + if (nt == null) return; + + if (string.IsNullOrEmpty(nt.Name)) { + if (nt.Rule != null && !string.IsNullOrEmpty(nt.Rule.Name)) + nt.Name = nt.Rule.Name; + else + nt.Name = "Unnamed" + (_unnamedCount++); + } + if (nt.Rule == null) + _language.Errors.AddAndThrow(GrammarErrorLevel.Error, null, Resources.ErrNtRuleIsNull, nt.Name); + //check all child elements + foreach (BnfTermList elemList in nt.Rule.Data) + for (int i = 0; i < elemList.Count; i++) { + BnfTerm child = elemList[i]; + if (child == null) { + _language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrRuleContainsNull, nt.Name, i); + continue; //for i loop + } + //Check for nested expression - convert to non-terminal + BnfExpression expr = child as BnfExpression; + if (expr != null) { + child = new NonTerminal(null, expr); + elemList[i] = child; + } + CollectTermsRecursive(child); + }//for i + }//method + + private void FillOperatorReportGroup() { + foreach(var group in _grammar.TermReportGroups) + if(group.GroupType == TermReportGroupType.Operator) { + foreach(var term in _grammarData.Terminals) + if (term.Flags.IsSet(TermFlags.IsOperator)) + group.Terminals.Add(term); + return; + } + } + + private void InitTermLists() { + //Collect terminals and NonTerminals + var empty = _grammar.Empty; + foreach (BnfTerm term in _grammarData.AllTerms) { //remember - we may have hints, so it's not only terminals and non-terminals + if (term is NonTerminal) _grammarData.NonTerminals.Add((NonTerminal)term); + if (term is Terminal && term != empty) _grammarData.Terminals.Add((Terminal)term); + } + //Mark keywords - any "word" symbol directly mentioned in the grammar + foreach (var term in _grammarData.Terminals) { + var symTerm = term as KeyTerm; + if (symTerm == null) continue; + if (!string.IsNullOrEmpty(symTerm.Text) && char.IsLetter(symTerm.Text[0])) + symTerm.SetFlag(TermFlags.IsKeyword); + }//foreach term + //Init all terms + foreach (var term in _grammarData.AllTerms) + term.Init(_grammarData); + }//method + + private void CreateProductions() { + _lastItemId = 0; + //CheckWrapTailHints() method may add non-terminals on the fly, so we have to use for loop here (not foreach) + foreach (var nt in _grammarData.NonTerminals) { + nt.Productions.Clear(); + //Get data (sequences) from both Rule and ErrorRule + BnfExpressionData allData = new BnfExpressionData(); + allData.AddRange(nt.Rule.Data); + if (nt.ErrorRule != null) + allData.AddRange(nt.ErrorRule.Data); + //actually create productions for each sequence + foreach (BnfTermList prodOperands in allData) { + Production prod = CreateProduction(nt, prodOperands); + nt.Productions.Add(prod); + } //foreach prodOperands + } + } + + private Production CreateProduction(NonTerminal lvalue, BnfTermList operands) { + Production prod = new Production(lvalue); + GrammarHintList hints = null; + //create RValues list skipping Empty terminal and collecting grammar hints + foreach (BnfTerm operand in operands) { + if (operand == _grammar.Empty) + continue; + //Collect hints as we go - they will be added to the next non-hint element + GrammarHint hint = operand as GrammarHint; + if (hint != null) { + if (hints == null) hints = new GrammarHintList(); + hints.Add(hint); + continue; + } + //Add the operand and create LR0 Item + prod.RValues.Add(operand); + prod.LR0Items.Add(new LR0Item(_lastItemId++, prod, prod.RValues.Count - 1, hints)); + hints = null; + }//foreach operand + //set the flags + if (prod.RValues.Count == 0) + prod.Flags |= ProductionFlags.IsEmpty; + //Add final LRItem + ComputeProductionFlags(prod); + prod.LR0Items.Add(new LR0Item(_lastItemId++, prod, prod.RValues.Count, hints)); + return prod; + } + + private void ComputeProductionFlags(Production production) { + production.Flags = ProductionFlags.None; + foreach (var rv in production.RValues) { + //Check if it is a Terminal or Error element + var t = rv as Terminal; + if (t != null) { + production.Flags |= ProductionFlags.HasTerminals; + if (t.Category == TokenCategory.Error) production.Flags |= ProductionFlags.IsError; + } + if(rv.Flags.IsSet(TermFlags.IsPunctuation)) continue; + }//foreach + }//method + + private static void ComputeNonTerminalsNullability(GrammarData data) { + var undecided = data.NonTerminals; + while (undecided.Count > 0) { + var newUndecided = new NonTerminalSet(); + foreach (NonTerminal nt in undecided) + if (!ComputeNullability(nt)) + newUndecided.Add(nt); + if (undecided.Count == newUndecided.Count) return; //we didn't decide on any new, so we're done + undecided = newUndecided; + }//while + } + + private static bool ComputeNullability(NonTerminal nonTerminal) { + foreach (Production prod in nonTerminal.Productions) { + if (prod.RValues.Count == 0) { + nonTerminal.SetFlag(TermFlags.IsNullable); + return true; //decided, Nullable + }//if + //If production has terminals, it is not nullable and cannot contribute to nullability + if (prod.Flags.IsSet(ProductionFlags.HasTerminals)) continue; + //Go thru all elements of production and check nullability + bool allNullable = true; + foreach (BnfTerm child in prod.RValues) { + allNullable &= child.Flags.IsSet(TermFlags.IsNullable); + }//foreach child + if (allNullable) { + nonTerminal.SetFlag(TermFlags.IsNullable); + return true; + } + }//foreach prod + return false; //cannot decide + } + + private static void ComputeTailsNullability(GrammarData data) { + foreach (var nt in data.NonTerminals) { + foreach (var prod in nt.Productions) { + var count = prod.LR0Items.Count; + for (int i = count - 1; i >= 0; i--) { + var item = prod.LR0Items[i]; + item.TailIsNullable = true; + if (item.Current == null) continue; + if (!item.Current.Flags.IsSet(TermFlags.IsNullable)) + break; //for i + }//for i + }//foreach prod + } + } + + #region Grammar Validation + private void ValidateGrammar() { + var createAst = _grammar.LanguageFlags.IsSet(LanguageFlags.CreateAst); + var invalidTransSet = new NonTerminalSet(); + foreach(var nt in _grammarData.NonTerminals) { + if(nt.Flags.IsSet(TermFlags.IsTransient)) { + //List non-terminals cannot be marked transient - otherwise there may be some ambiguities and inconsistencies + if (nt.Flags.IsSet(TermFlags.IsList)) + _language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrListCannotBeTransient, nt.Name); + //Count number of non-punctuation child nodes in each production + foreach(var prod in nt.Productions) + if (CountNonPunctuationTerms(prod) > 1) invalidTransSet.Add(nt); + }//if transient + //Validate error productions + foreach(var prod in nt.Productions) + if(prod.Flags.IsSet(ProductionFlags.IsError)) { + var lastTerm = prod.RValues[prod.RValues.Count -1]; + if (!(lastTerm is Terminal) || lastTerm == _grammar.SyntaxError) + _language.Errors.Add(GrammarErrorLevel.Warning, null, Resources.ErrLastTermOfErrorProd, nt.Name); + // "The last term of error production must be a terminal. NonTerminal: {0}" + }//foreach prod + }//foreac nt + + if (invalidTransSet.Count > 0) + _language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTransientNtMustHaveOneTerm,invalidTransSet.ToString()); + }//method + + private int CountNonPunctuationTerms(Production production) { + int count = 0; + foreach(var rvalue in production.RValues) + if (!rvalue.Flags.IsSet(TermFlags.IsPunctuation)) count++; + return count; + } + #endregion + + }//class +} diff --git a/Irony/Parsing/Data/Construction/LanguageDataBuilder.cs b/Irony/Parsing/Data/Construction/LanguageDataBuilder.cs new file mode 100644 index 0000000..f478641 --- /dev/null +++ b/Irony/Parsing/Data/Construction/LanguageDataBuilder.cs @@ -0,0 +1,66 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; + +namespace Irony.Parsing.Construction { + internal class LanguageDataBuilder { + + internal LanguageData Language; + Grammar _grammar; + + public LanguageDataBuilder(LanguageData language) { + Language = language; + _grammar = Language.Grammar; + } + + public bool Build() { + var sw = new Stopwatch(); + try { + if (_grammar.Root == null) + Language.Errors.AddAndThrow(GrammarErrorLevel.Error, null, Resources.ErrRootNotSet); + sw.Start(); + var gbld = new GrammarDataBuilder(Language); + gbld.Build(); + //Just in case grammar author wants to customize something... + _grammar.OnGrammarDataConstructed(Language); + var sbld = new ScannerDataBuilder(Language); + sbld.Build(); + var pbld = new ParserDataBuilder(Language); + pbld.Build(); + Validate(); + //call grammar method, a chance to tweak the automaton + _grammar.OnLanguageDataConstructed(Language); + return true; + } catch (GrammarErrorException) { + return false; //grammar error should be already added to Language.Errors collection + } finally { + Language.ErrorLevel = Language.Errors.GetMaxLevel(); + sw.Stop(); + Language.ConstructionTime = sw.ElapsedMilliseconds; + } + + } + + #region Language Data Validation + private void Validate() { + + }//method + #endregion + + + }//class +} diff --git a/Irony/Parsing/Data/Construction/ParserDataBuilder.cs b/Irony/Parsing/Data/Construction/ParserDataBuilder.cs new file mode 100644 index 0000000..0af61da --- /dev/null +++ b/Irony/Parsing/Data/Construction/ParserDataBuilder.cs @@ -0,0 +1,422 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Diagnostics; + +namespace Irony.Parsing.Construction { + + // Methods constructing LALR automaton. + // See _about_parser_construction.txt file in this folder for important comments + + internal partial class ParserDataBuilder { + LanguageData _language; + ParserData _data; + Grammar _grammar; + ParserStateHash _stateHash = new ParserStateHash(); + + internal ParserDataBuilder(LanguageData language) { + _language = language; + _grammar = _language.Grammar; + } + + public void Build() { + _stateHash.Clear(); + _data = _language.ParserData; + CreateParserStates(); + var itemsNeedLookaheads = GetReduceItemsInInadequateState(); + ComputeTransitions(itemsNeedLookaheads); + ComputeLookaheads(itemsNeedLookaheads); + ComputeStatesExpectedTerminals(); + ComputeConflicts(); + ApplyHints(); + HandleUnresolvedConflicts(); + CreateRemainingReduceActions(); + //Create error action - if it is not created yet by some hint or custom code + if (_data.ErrorAction == null) + _data.ErrorAction = new ErrorRecoveryParserAction(); + }//method + + #region Creating parser states + private void CreateParserStates() { + var grammarData = _language.GrammarData; + + //1. Base automaton: create states for main augmented root for the grammar + _data.InitialState = CreateInitialState(grammarData.AugmentedRoot); + ExpandParserStateList(0); + CreateAcceptAction(_data.InitialState, grammarData.AugmentedRoot); + + //2. Expand automaton: add parser states from additional roots + foreach(var augmRoot in grammarData.AugmentedSnippetRoots) { + var initialState = CreateInitialState(augmRoot); + ExpandParserStateList(_data.States.Count - 1); //start with just added state - it is the last state in the list + CreateAcceptAction(initialState, augmRoot); + } + } + + private void CreateAcceptAction(ParserState initialState, NonTerminal augmentedRoot) { + var root = augmentedRoot.Productions[0].RValues[0]; + var shiftAction = initialState.Actions[root] as ShiftParserAction; + var shiftOverRootState = shiftAction.NewState; + shiftOverRootState.Actions[_grammar.Eof] = new AcceptParserAction(); + } + + + private ParserState CreateInitialState(NonTerminal augmentedRoot) { + //for an augmented root there is an initial production "Root' -> .Root"; so we need the LR0 item at 0 index + var iniItemSet = new LR0ItemSet(); + iniItemSet.Add(augmentedRoot.Productions[0].LR0Items[0]); + var initialState = FindOrCreateState(iniItemSet); + var rootNt = augmentedRoot.Productions[0].RValues[0] as NonTerminal; + _data.InitialStates[rootNt] = initialState; + return initialState; + } + + private void ExpandParserStateList(int initialIndex) { + // Iterate through states (while new ones are created) and create shift transitions and new states + for (int index = initialIndex; index < _data.States.Count; index++) { + var state = _data.States[index]; + //Get all possible shifts + foreach (var term in state.BuilderData.ShiftTerms) { + var shiftItems = state.BuilderData.ShiftItems.SelectByCurrent(term); + //Get set of shifted cores and find/create target state + var shiftedCoreItems = shiftItems.GetShiftedCores(); + var newState = FindOrCreateState(shiftedCoreItems); + //Create shift action + var newAction = new ShiftParserAction(term, newState); + state.Actions[term] = newAction; + //Link items in old/new states + foreach (var shiftItem in shiftItems) { + shiftItem.ShiftedItem = newState.BuilderData.AllItems.FindByCore(shiftItem.Core.ShiftedItem); + }//foreach shiftItem + }//foreach term + } //for index + }//method + + private ParserState FindOrCreateState(LR0ItemSet coreItems) { + string key = ComputeLR0ItemSetKey(coreItems); + ParserState state; + if (_stateHash.TryGetValue(key, out state)) + return state; + //create new state + state = new ParserState("S" + _data.States.Count); + state.BuilderData = new ParserStateData(state, coreItems); + _data.States.Add(state); + _stateHash[key] = state; + return state; + } + + #endregion + + #region Compute transitions, lookbacks, lookaheads + //We compute only transitions that are really needed to compute lookaheads in inadequate states. + // We start with reduce items in inadequate state and find their lookbacks - this is initial list of transitions. + // Then for each transition in the list we check if it has items with nullable tails; for those items we compute + // lookbacks - these are new or already existing transitons - and so on, we repeat the operation until no new transitions + // are created. + private void ComputeTransitions(LRItemSet forItems) { + var newItemsNeedLookbacks = forItems; + while(newItemsNeedLookbacks.Count > 0) { + var newTransitions = CreateLookbackTransitions(newItemsNeedLookbacks); + newItemsNeedLookbacks = SelectNewItemsThatNeedLookback(newTransitions); + } + } + + private LRItemSet SelectNewItemsThatNeedLookback(TransitionList transitions) { + //Select items with nullable tails that don't have lookbacks yet + var items = new LRItemSet(); + foreach(var trans in transitions) + foreach(var item in trans.Items) + if (item.Core.TailIsNullable && item.Lookbacks.Count == 0) //only if it does not have lookbacks yet + items.Add(item); + return items; + } + + private LRItemSet GetReduceItemsInInadequateState() { + var result = new LRItemSet(); + foreach(var state in _data.States) { + if (state.BuilderData.IsInadequate) + result.UnionWith(state.BuilderData.ReduceItems); + } + return result; + } + + private TransitionList CreateLookbackTransitions(LRItemSet sourceItems) { + var newTransitions = new TransitionList(); + //Build set of initial cores - this is optimization for performance + //We need to find all initial items in all states that shift into one of sourceItems + // Each such initial item would have the core from the "initial" cores set that we build from source items. + var iniCores = new LR0ItemSet(); + foreach(var sourceItem in sourceItems) + iniCores.Add(sourceItem.Core.Production.LR0Items[0]); + //find + foreach(var state in _data.States) { + foreach(var iniItem in state.BuilderData.InitialItems) { + if (!iniCores.Contains(iniItem.Core)) continue; + var iniItemNt = iniItem.Core.Production.LValue; // iniItem's non-terminal (left side of production) + Transition lookback = null; // local var for lookback - transition over iniItemNt + var currItem = iniItem; // iniItem is initial item for all currItem's in the shift chain. + while (currItem != null) { + if(sourceItems.Contains(currItem)) { + // We create transitions lazily, only when we actually need them. Check if we have iniItem's transition + // in local variable; if not, get it from state's transitions table; if not found, create it. + if(lookback == null && !state.BuilderData.Transitions.TryGetValue(iniItemNt, out lookback)) { + lookback = new Transition(state, iniItemNt); + newTransitions.Add(lookback); + } + //Now for currItem, either add trans to Lookbacks, or "include" it into currItem.Transition + // We need lookbacks ONLY for final items; for non-Final items we need proper Include lists on transitions + if (currItem.Core.IsFinal) + currItem.Lookbacks.Add(lookback); + else // if (currItem.Transition != null) + // Note: looks like checking for currItem.Transition is redundant - currItem is either: + // - Final - always the case for the first run of this method; + // - it has a transition after the first run, due to the way we select sourceItems list + // in SelectNewItemsThatNeedLookback (by transitions) + currItem.Transition.Include(lookback); + }//if + //move to next item + currItem = currItem.ShiftedItem; + }//while + }//foreach iniItem + }//foreach state + return newTransitions; + } + + private void ComputeLookaheads(LRItemSet forItems) { + foreach(var reduceItem in forItems) { + // Find all source states - those that contribute lookaheads + var sourceStates = new ParserStateSet(); + foreach(var lookbackTrans in reduceItem.Lookbacks) { + sourceStates.Add(lookbackTrans.ToState); + sourceStates.UnionWith(lookbackTrans.ToState.BuilderData.ReadStateSet); + foreach(var includeTrans in lookbackTrans.Includes) { + sourceStates.Add(includeTrans.ToState); + sourceStates.UnionWith(includeTrans.ToState.BuilderData.ReadStateSet); + }//foreach includeTrans + }//foreach lookbackTrans + //Now merge all shift terminals from all source states + foreach(var state in sourceStates) + reduceItem.Lookaheads.UnionWith(state.BuilderData.ShiftTerminals); + //Remove SyntaxError - it is pseudo terminal + if (reduceItem.Lookaheads.Contains(_grammar.SyntaxError)) + reduceItem.Lookaheads.Remove(_grammar.SyntaxError); + //Sanity check + if (reduceItem.Lookaheads.Count == 0) + _language.Errors.Add(GrammarErrorLevel.InternalError, reduceItem.State, "Reduce item '{0}' in state {1} has no lookaheads.", reduceItem.Core, reduceItem.State); + }//foreach reduceItem + }//method + + #endregion + + #region Analyzing and resolving conflicts + private void ComputeConflicts() { + foreach(var state in _data.States) { + if(!state.BuilderData.IsInadequate) + continue; + //first detect conflicts + var stateData = state.BuilderData; + stateData.Conflicts.Clear(); + var allLkhds = new BnfTermSet(); + //reduce/reduce -------------------------------------------------------------------------------------- + foreach(var item in stateData.ReduceItems) { + foreach(var lkh in item.Lookaheads) { + if(allLkhds.Contains(lkh)) + state.BuilderData.Conflicts.Add(lkh); + allLkhds.Add(lkh); + }//foreach lkh + }//foreach item + + //shift/reduce --------------------------------------------------------------------------------------- + foreach(var term in stateData.ShiftTerminals) + if(allLkhds.Contains(term)) { + stateData.Conflicts.Add(term); + } + } + }//method + + private void ApplyHints() { + foreach (var state in _data.States) { + var stateData = state.BuilderData; + //Add automatic precedence hints + if (stateData.Conflicts.Count > 0) + foreach (var conflict in stateData.Conflicts.ToList()) + if (conflict.Flags.IsSet(TermFlags.IsOperator)) { + //Find any reduce item with this lookahead and add PrecedenceHint + var reduceItem = stateData.ReduceItems.SelectByLookahead(conflict).First(); + var precHint = new PrecedenceHint(); + reduceItem.Core.Hints.Add(precHint); + } + // Apply (activate) hints - these should resolve conflicts as well + foreach (var item in state.BuilderData.AllItems) + foreach (var hint in item.Core.Hints) + hint.Apply(_language, item); + + }//foreach + }//method + + //Resolve to default actions + private void HandleUnresolvedConflicts() { + foreach (var state in _data.States) { + if (state.BuilderData.Conflicts.Count == 0) + continue; + var shiftReduceConflicts = state.BuilderData.GetShiftReduceConflicts(); + var reduceReduceConflicts = state.BuilderData.GetReduceReduceConflicts(); + var stateData = state.BuilderData; + if (shiftReduceConflicts.Count > 0) + _language.Errors.Add(GrammarErrorLevel.Conflict, state, Resources.ErrSRConflict, state, shiftReduceConflicts.ToString()); + if (reduceReduceConflicts.Count > 0) + _language.Errors.Add(GrammarErrorLevel.Conflict, state, Resources.ErrRRConflict, state, reduceReduceConflicts.ToString()); + //Create default actions for these conflicts. For shift-reduce, default action is shift, and shift action already + // exist for all shifts from the state, so we don't need to do anything, only report it + //For reduce-reduce create reduce actions for the first reduce item (whatever comes first in the set). + foreach (var conflict in reduceReduceConflicts) { + var reduceItems = stateData.ReduceItems.SelectByLookahead(conflict); + var firstProd = reduceItems.First().Core.Production; + var action = new ReduceParserAction(firstProd); + state.Actions[conflict] = action; + } + //stateData.Conflicts.Clear(); -- do not clear them, let the set keep the auto-resolved conflicts, may find more use for this later + } + } + + #endregion + + #region final actions: creating remaining reduce actions, computing expected terminals, cleaning up state data + //Create reduce actions for states with a single reduce item (and no shifts) + private void CreateRemainingReduceActions() { + foreach (var state in _data.States) { + if (state.DefaultAction != null) continue; + var stateData = state.BuilderData; + if (stateData.ShiftItems.Count == 0 && stateData.ReduceItems.Count == 1) { + state.DefaultAction = ReduceParserAction.Create(stateData.ReduceItems.First().Core.Production); + continue; //next state; if we have default reduce action, we don't need to fill actions dictionary for lookaheads + } + //create actions + foreach (var item in state.BuilderData.ReduceItems) { + var action = ReduceParserAction.Create(item.Core.Production); + foreach (var lkh in item.Lookaheads) { + if (state.Actions.ContainsKey(lkh)) continue; + state.Actions[lkh] = action; + } + }//foreach item + + }//foreach state + } + + //Note that for states with a single reduce item the result is empty + private void ComputeStatesExpectedTerminals() { + foreach (var state in _data.States) { + state.ExpectedTerminals.UnionWith(state.BuilderData.ShiftTerminals); + //Add lookaheads from reduce items + foreach (var reduceItem in state.BuilderData.ReduceItems) + state.ExpectedTerminals.UnionWith(reduceItem.Lookaheads); + RemoveTerminals(state.ExpectedTerminals, _grammar.SyntaxError, _grammar.Eof); + }//foreach state + } + + private void RemoveTerminals(TerminalSet terms, params Terminal[] termsToRemove) { + foreach(var termToRemove in termsToRemove) + if (terms.Contains(termToRemove)) terms.Remove(termToRemove); + } + + public void CleanupStateData() { + foreach (var state in _data.States) + state.ClearData(); + } + #endregion + + #region Utilities: ComputeLR0ItemSetKey + //Parser states are distinguished by the subset of kernel LR0 items. + // So when we derive new LR0-item list by shift operation, + // we need to find out if we have already a state with the same LR0Item list. + // We do it by looking up in a state hash by a key - [LR0 item list key]. + // Each list's key is a concatenation of items' IDs separated by ','. + // Before producing the key for a list, the list must be sorted; + // thus we garantee one-to-one correspondence between LR0Item sets and keys. + // And of course, we count only kernel items (with dot NOT in the first position). + public static string ComputeLR0ItemSetKey(LR0ItemSet items) { + if (items.Count == 0) return string.Empty; + //Copy non-initial items to separate list, and then sort it + LR0ItemList itemList = new LR0ItemList(); + foreach (var item in items) + itemList.Add(item); + //quick shortcut + if (itemList.Count == 1) + return itemList[0].ID.ToString(); + itemList.Sort(CompareLR0Items); //Sort by ID + //now build the key + StringBuilder sb = new StringBuilder(100); + foreach (LR0Item item in itemList) { + sb.Append(item.ID); + sb.Append(","); + }//foreach + return sb.ToString(); + } + + private static int CompareLR0Items(LR0Item x, LR0Item y) { + if (x.ID < y.ID) return -1; + if (x.ID == y.ID) return 0; + return 1; + } + #endregion + + + #region comments + // Computes set of expected terms in a parser state. While there may be extended list of symbols expected at some point, + // we want to reorganize and reduce it. For example, if the current state expects all arithmetic operators as an input, + // it would be better to not list all operators (+, -, *, /, etc) but simply put "operator" covering them all. + // To achieve this grammar writer can group operators (or any other terminals) into named groups using Grammar's methods + // AddTermReportGroup, AddNoReportGroup etc. Then instead of reporting each operator separately, Irony would include + // a single "group name" to represent them all. + // The "expected report set" is not computed during parser construction (it would bite considerable time), but on demand during parsing, + // when error is detected and the expected set is actually needed for error message. + // Multi-threading concerns. When used in multi-threaded environment (web server), the LanguageData would be shared in + // application-wide cache to avoid rebuilding the parser data on every request. The LanguageData is immutable, except + // this one case - the expected sets are constructed late by CoreParser on the when-needed basis. + // We don't do any locking here, just compute the set and on return from this function the state field is assigned. + // We assume that this field assignment is an atomic, concurrency-safe operation. The worst thing that might happen + // is "double-effort" when two threads start computing the same set around the same time, and the last one to finish would + // leave its result in the state field. + #endregion + internal static StringSet ComputeGroupedExpectedSetForState(Grammar grammar, ParserState state) { + var terms = new TerminalSet(); + terms.UnionWith(state.ExpectedTerminals); + var result = new StringSet(); + //Eliminate no-report terminals + foreach (var group in grammar.TermReportGroups) + if (group.GroupType == TermReportGroupType.DoNotReport) + terms.ExceptWith(group.Terminals); + //Add normal and operator groups + foreach (var group in grammar.TermReportGroups) + if ((group.GroupType == TermReportGroupType.Normal || group.GroupType == TermReportGroupType.Operator) && + terms.Overlaps(group.Terminals)) { + result.Add(group.Alias); + terms.ExceptWith(group.Terminals); + } + //Add remaining terminals "as is" + foreach (var terminal in terms) + result.Add(terminal.ErrorAlias); + return result; + } + + + }//class + + +}//namespace + + diff --git a/Irony/Parsing/Data/Construction/ParserDataBuilder_HelperClasses.cs b/Irony/Parsing/Data/Construction/ParserDataBuilder_HelperClasses.cs new file mode 100644 index 0000000..a683427 --- /dev/null +++ b/Irony/Parsing/Data/Construction/ParserDataBuilder_HelperClasses.cs @@ -0,0 +1,289 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using System.Linq; + +//Helper data classes for ParserDataBuilder +// Note about using LRItemSet vs LRItemList. +// It appears that in many places the LRItemList would be a better (and faster) choice than LRItemSet. +// Many of the sets are actually lists and don't require hashset's functionality. +// But surprisingly, using LRItemSet proved to have much better performance (twice faster for lookbacks/lookaheads computation), so LRItemSet +// is used everywhere. +namespace Irony.Parsing.Construction { + + public class ParserStateData { + public readonly ParserState State; + public readonly LRItemSet AllItems = new LRItemSet(); + public readonly LRItemSet ShiftItems = new LRItemSet(); + public readonly LRItemSet ReduceItems = new LRItemSet(); + public readonly LRItemSet InitialItems = new LRItemSet(); + public readonly BnfTermSet ShiftTerms = new BnfTermSet(); + public readonly TerminalSet ShiftTerminals = new TerminalSet(); + public readonly TerminalSet Conflicts = new TerminalSet(); + public readonly bool IsInadequate; + public LR0ItemSet AllCores = new LR0ItemSet(); + + //used for creating canonical states from core set + public ParserStateData(ParserState state, LR0ItemSet kernelCores) { + State = state; + foreach (var core in kernelCores) + AddItem(core); + IsInadequate = ReduceItems.Count > 1 || ReduceItems.Count == 1 && ShiftItems.Count > 0; + } + + public void AddItem(LR0Item core) { + //Check if a core had been already added. If yes, simply return + if(!AllCores.Add(core))return ; + //Create new item, add it to AllItems, InitialItems, ReduceItems or ShiftItems + var item = new LRItem(State, core); + AllItems.Add(item); + if (item.Core.IsFinal) + ReduceItems.Add(item); + else + ShiftItems.Add(item); + if (item.Core.IsInitial) + InitialItems.Add(item); + if (core.IsFinal) return; + //Add current term to ShiftTerms + if (!ShiftTerms.Add(core.Current)) return; + if (core.Current is Terminal) + ShiftTerminals.Add(core.Current as Terminal); + //If current term (core.Current) is a new non-terminal, expand it + var currNt = core.Current as NonTerminal; + if (currNt == null) return; + foreach(var prod in currNt.Productions) + AddItem(prod.LR0Items[0]); + }//method + + public TransitionTable Transitions { + get { + if(_transitions == null) + _transitions = new TransitionTable(); + return _transitions; + } + } TransitionTable _transitions; + + //A set of states reachable through shifts over nullable non-terminals. Computed on demand + public ParserStateSet ReadStateSet { + get { + if(_readStateSet == null) { + _readStateSet = new ParserStateSet(); + foreach(var shiftTerm in State.BuilderData.ShiftTerms) + if (shiftTerm.Flags.IsSet(TermFlags.IsNullable)) { + var shift = State.Actions[shiftTerm] as ShiftParserAction; + var targetState = shift.NewState; + _readStateSet.Add(targetState); + _readStateSet.UnionWith(targetState.BuilderData.ReadStateSet); //we shouldn't get into loop here, the chain of reads is finite + } + }//if + return _readStateSet; + } + } ParserStateSet _readStateSet; + + public ParserState GetNextState(BnfTerm shiftTerm) { + var shift = ShiftItems.FirstOrDefault(item => item.Core.Current == shiftTerm); + if (shift == null) return null; + return shift.ShiftedItem.State; + } + + public TerminalSet GetShiftReduceConflicts() { + var result = new TerminalSet(); + result.UnionWith(Conflicts); + result.IntersectWith(ShiftTerminals); + return result; + } + public TerminalSet GetReduceReduceConflicts() { + var result = new TerminalSet(); + result.UnionWith(Conflicts); + result.ExceptWith(ShiftTerminals); + return result; + } + + }//class + + //An object representing inter-state transitions. Defines Includes, IncludedBy that are used for efficient lookahead computation + public class Transition { + public readonly ParserState FromState; + public readonly ParserState ToState; + public readonly NonTerminal OverNonTerminal; + public readonly LRItemSet Items; + public readonly TransitionSet Includes = new TransitionSet(); + public readonly TransitionSet IncludedBy = new TransitionSet(); + int _hashCode; + + public Transition(ParserState fromState, NonTerminal overNonTerminal) { + FromState = fromState; + OverNonTerminal = overNonTerminal; + var shiftItem = fromState.BuilderData.ShiftItems.First(item=>item.Core.Current == overNonTerminal); + ToState = FromState.BuilderData.GetNextState(overNonTerminal); + _hashCode = unchecked(FromState.GetHashCode() - overNonTerminal.GetHashCode()); + FromState.BuilderData.Transitions.Add(overNonTerminal, this); + Items = FromState.BuilderData.ShiftItems.SelectByCurrent(overNonTerminal); + foreach(var item in Items) { + item.Transition = this; + } + + }//constructor + + public void Include(Transition other) { + if (other == this) return; + if (!IncludeTransition(other)) return; + //include children + foreach(var child in other.Includes) { + IncludeTransition(child); + } + } + private bool IncludeTransition(Transition other) { + if (!Includes.Add(other)) return false; + other.IncludedBy.Add(this); + //propagate "up" + foreach(var incBy in IncludedBy) + incBy.IncludeTransition(other); + return true; + } + + public override string ToString() { + return FromState.Name + " -> (over " + OverNonTerminal.Name + ") -> " + ToState.Name; + } + public override int GetHashCode() { + return _hashCode; + } + }//class + + public class TransitionSet : HashSet { } + public class TransitionList : List { } + public class TransitionTable : Dictionary { } + + public class LRItem { + public readonly ParserState State; + public readonly LR0Item Core; + //these properties are used in lookahead computations + public LRItem ShiftedItem; + public Transition Transition; + int _hashCode; + + //Lookahead info for reduce items + public TransitionSet Lookbacks = new TransitionSet(); + public TerminalSet Lookaheads = new TerminalSet(); + + public LRItem(ParserState state, LR0Item core) { + State = state; + Core = core; + _hashCode = unchecked(state.GetHashCode() + core.GetHashCode()); + } + public override string ToString() { + return Core.ToString(); + } + public override int GetHashCode() { + return _hashCode; + } + + public TerminalSet GetLookaheadsInConflict() { + var lkhc = new TerminalSet(); + lkhc.UnionWith(Lookaheads); + lkhc.IntersectWith(State.BuilderData.Conflicts); + return lkhc; + } + + }//LRItem class + + public class LRItemList : List {} + + public class LRItemSet : HashSet { + + public LRItem FindByCore(LR0Item core) { + foreach (LRItem item in this) + if (item.Core == core) return item; + return null; + } + public LRItemSet SelectByCurrent(BnfTerm current) { + var result = new LRItemSet(); + foreach (var item in this) + if (item.Core.Current == current) + result.Add(item); + return result; + } + + public LR0ItemSet GetShiftedCores() { + var result = new LR0ItemSet(); + foreach (var item in this) + if (item.Core.ShiftedItem != null) + result.Add(item.Core.ShiftedItem); + return result; + } + public LRItemSet SelectByLookahead(Terminal lookahead) { + var result = new LRItemSet(); + foreach (var item in this) + if (item.Lookaheads.Contains(lookahead)) + result.Add(item); + return result; + } + + }//class + + public partial class LR0Item { + public readonly Production Production; + public readonly int Position; + public readonly BnfTerm Current; + public bool TailIsNullable; + public GrammarHintList Hints = new GrammarHintList(); + + //automatically generated IDs - used for building keys for lists of kernel LR0Items + // which in turn are used to quickly lookup parser states in hash + internal readonly int ID; + + public LR0Item(int id, Production production, int position, GrammarHintList hints) { + ID = id; + Production = production; + Position = position; + Current = (Position < Production.RValues.Count) ? Production.RValues[Position] : null; + if (hints != null) + Hints.AddRange(hints); + _hashCode = ID.ToString().GetHashCode(); + }//method + + public LR0Item ShiftedItem { + get { + if (Position >= Production.LR0Items.Count - 1) + return null; + else + return Production.LR0Items[Position + 1]; + } + } + public bool IsKernel { + get { return Position > 0; } + } + public bool IsInitial { + get { return Position == 0; } + } + public bool IsFinal { + get { return Position == Production.RValues.Count; } + } + public override string ToString() { + return Production.ProductionToString(Production, Position); + } + public override int GetHashCode() { + return _hashCode; + } int _hashCode; + + }//LR0Item + + public class LR0ItemList : List { } + public class LR0ItemSet : HashSet { } + + + +}//namespace diff --git a/Irony/Parsing/Data/Construction/ScannerDataBuilder.cs b/Irony/Parsing/Data/Construction/ScannerDataBuilder.cs new file mode 100644 index 0000000..12b2303 --- /dev/null +++ b/Irony/Parsing/Data/Construction/ScannerDataBuilder.cs @@ -0,0 +1,130 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Irony.Parsing.Construction { + internal class ScannerDataBuilder { + LanguageData _language; + Grammar _grammar; + GrammarData _grammarData; + ScannerData _data; + + internal ScannerDataBuilder(LanguageData language) { + _language = language; + _grammar = _language.Grammar; + _grammarData = language.GrammarData; + } + + internal void Build() { + _data = _language.ScannerData; + InitMultilineTerminalsList(); + ProcessNonGrammarTerminals(); + BuildTerminalsLookupTable(); + } + + private void InitMultilineTerminalsList() { + foreach (var terminal in _grammarData.Terminals) { + if (terminal.Flags.IsSet(TermFlags.IsNonScanner)) continue; + if (terminal.Flags.IsSet(TermFlags.IsMultiline)) { + _data.MultilineTerminals.Add(terminal); + terminal.MultilineIndex = (byte)(_data.MultilineTerminals.Count); + } + } + } + + private void ProcessNonGrammarTerminals() { + foreach(var term in _grammar.NonGrammarTerminals) { + var firsts = term.GetFirsts(); + if(firsts == null || firsts.Count == 0) { + _language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTerminalHasEmptyPrefix, term.Name); + continue; + } + AddTerminalToLookup(_data.NonGrammarTerminalsLookup, term, firsts); + }//foreach term + + //sort each list + foreach(var list in _data.NonGrammarTerminalsLookup.Values) { + if(list.Count > 1) + list.Sort(Terminal.ByPriorityReverse); + }//foreach list + } + + private void BuildTerminalsLookupTable() { + foreach (Terminal term in _grammarData.Terminals) { + //Non-grammar terminals are scanned in a separate step, before regular terminals; so we don't include them here + if (term.Flags.IsSet(TermFlags.IsNonScanner | TermFlags.IsNonGrammar)) continue; + var firsts = term.GetFirsts(); + if (firsts == null || firsts.Count == 0) { + _grammarData.NoPrefixTerminals.Add(term); + continue; //foreach term + } + AddTerminalToLookup(_data.TerminalsLookup, term, firsts); + }//foreach term + + if (_grammarData.NoPrefixTerminals.Count > 0) { + //copy them to Scanner data + _data.NoPrefixTerminals.AddRange(_grammarData.NoPrefixTerminals); + // Sort in reverse priority order + _data.NoPrefixTerminals.Sort(Terminal.ByPriorityReverse); + //Now add Fallback terminals to every list, then sort lists by reverse priority + // so that terminal with higher priority comes first in the list + foreach (TerminalList list in _data.TerminalsLookup.Values) + foreach (var ft in _data.NoPrefixTerminals) + if (!list.Contains(ft)) + list.Add(ft); + }//if count > 0 + + //Finally sort every list in terminals lookup table + foreach (TerminalList list in _data.TerminalsLookup.Values) + if(list.Count > 1) + list.Sort(Terminal.ByPriorityReverse); + + }//method + + private void AddTerminalToLookup(TerminalLookupTable _lookup, Terminal term, IList firsts) { + foreach (string prefix in firsts) { + if(string.IsNullOrEmpty(prefix)) { + _language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTerminalHasEmptyPrefix, term.Name); + continue; + } + //Calculate hash key for the prefix + char firstChar = prefix[0]; + if(_grammar.CaseSensitive) + AddTerminalToLookupByFirstChar(_lookup, term, firstChar); + else { + AddTerminalToLookupByFirstChar(_lookup, term, char.ToLower(firstChar)); + AddTerminalToLookupByFirstChar(_lookup, term, char.ToUpper(firstChar)); + }//if + }//foreach prefix + + } + + private void AddTerminalToLookupByFirstChar(TerminalLookupTable _lookup, Terminal term, char firstChar) { + TerminalList currentList; + if (!_lookup.TryGetValue(firstChar, out currentList)) { + //if list does not exist yet, create it + currentList = new TerminalList(); + _lookup[firstChar] = currentList; + } + //add terminal to the list + if (!currentList.Contains(term)) + currentList.Add(term); + + } + + }//class + +}//namespace diff --git a/Irony/Parsing/Data/Construction/_about_parser_construction.txt b/Irony/Parsing/Data/Construction/_about_parser_construction.txt new file mode 100644 index 0000000..f6eed86 --- /dev/null +++ b/Irony/Parsing/Data/Construction/_about_parser_construction.txt @@ -0,0 +1,45 @@ + About parser construction algorithm in general + We follow DeRemer-Penello's algorithm, as it is described in Grune, Jacobs "Parsing Techniques" 2nd ed, section 9.7, p. 309. + There are a few differences: + 1. We compute lookbacks and transitions "on-demand" - only those that are actually needed for computing lookaheads in + reduce items in inadequate states. We start with reduce items in inadequate states - those are the only items that need lookaheads. + We then find all lookbacks (transitions) for these items. Then for each transition we find which ones need to "include" other parent + transitions - and compute this. And so on, until all transitions are created and linked through Include relationships + 2. We propagate Include relation between transitions immediately, when we add an include relation of one transition to another. See + Transition.Include method. Thus we avoid an extra step of "Transitive closure" of Include relation. See note about efficiency below. + 3. We don't use Reads and DirectRead relation between transitions. "Reads" relation + between transitions is replaced by Reads relation between states. So state A READS state B if you can move from state A to state B + using shifts over nullable non-terminals. ParserStateData.ReadStateSet contains all states that current state Reads. ReadStateSet + is computed on-demand, and all reads are immediately propagated through transitive chain - see source code of the method. + For DirectReads set for a transition in DeRemer-Penello - we use a state.ShiftTerminals set of the target state of the transition + - obviously this is the same set. + + Note about immediate Include propagation + I think that the method with immediate Includes propagation is as efficient as it can be, and using Transitive Closure optimization + through Srongly-Connected Components (SCC) algorithm would not be much faster. With immediate propagation we attempt to add + a transition to Includes set of another transition only once and stop propagation of the transition further down the chain if it is + already there. Essentially, we don't waste time propagating sets of transitions through chains of Includes if the transitions are + alredy there, propagated through different route. This is what SCC method is trying to mitigate - repeated propagation of transitions - + but this is not happening in our implementation. Maybe I'm mistaken, this is a guess, not a formal proof - let me know if you see + any flaws in my reasoning. + + About computing ExpectedTerminals set for parser states. + ExpectedTerms is a property of ParserState and contains all Terminals that parser expects in this state. This set is used by Scanner + to filter out terminals for the next token when it has a choice of more than one for a current input character. + (This is called Scanner-Parser link facility). + The question now is how to compute this set. There are are several kinds of Parser states: + 1. Containing shift items only. The ExpectedSet is a union of all "current" terms of all shift items. State.BuilderData.ShiftTerms + already contains this set - easy case. + 2. Containing shift AND reduce items. This is inadequate set. The expected set is a union of all current terms of shift items + (like in previous case) plus all lookaheads of reduce items. Reduce items have lookaheads computed, because it is an inadequate state. + 3. Containing 2 or more reduce items - this is again an inadequate state, each reduce items has lookaheads computed, so expected set + is a union of lookaheads of reduce items. + 4. Containing a single reduce item. This is a problem case. The state is not inadequate - we do not compute lookaheads for a single + reduce item, as there is no need for them - only a single action is possible. + The solution for the last case with a single reduce item is the following: we do not compute ExpectedSet for such states, but make sure + that scanner-parser link is never activated in this case. We do it in Parser code by NOT reading the next token from Scanner when + current state has a single reduce action (DefaultReduceAction property is not null). We do not read next token because it is not needed + for finding an action - there is one single possible action anyway. As a result the Scanner would never start scanning a new token + when parser in this single-reduce state - and therefore scanner would not invoke the parser-scanner link. + See CoreParser.ExecuteAction method for details. + diff --git a/Irony/Parsing/Data/GrammarData.cs b/Irony/Parsing/Data/GrammarData.cs new file mode 100644 index 0000000..3550269 --- /dev/null +++ b/Irony/Parsing/Data/GrammarData.cs @@ -0,0 +1,41 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + //GrammarData is a container for all basic info about the grammar + // GrammarData is a field in LanguageData object. + public class GrammarData { + public readonly LanguageData Language; + public readonly Grammar Grammar; + public NonTerminal AugmentedRoot; + public NonTerminalSet AugmentedSnippetRoots = new NonTerminalSet(); + public readonly BnfTermSet AllTerms = new BnfTermSet(); + public readonly TerminalSet Terminals = new TerminalSet(); + public readonly NonTerminalSet NonTerminals = new NonTerminalSet(); + public TerminalSet NoPrefixTerminals = new TerminalSet(); //Terminals that have no limited set of prefixes + + public GrammarData(LanguageData language) { + Language = language; + Grammar = language.Grammar; + } + + }//class + + + +}//namespace diff --git a/Irony/Parsing/Data/LanguageData.cs b/Irony/Parsing/Data/LanguageData.cs new file mode 100644 index 0000000..4b23d9c --- /dev/null +++ b/Irony/Parsing/Data/LanguageData.cs @@ -0,0 +1,45 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing.Construction; + +namespace Irony.Parsing { + public class LanguageData { + public readonly Grammar Grammar; + public readonly GrammarData GrammarData; + public readonly ParserData ParserData; + public readonly ScannerData ScannerData; + public readonly GrammarErrorList Errors = new GrammarErrorList(); + public GrammarErrorLevel ErrorLevel = GrammarErrorLevel.NoError; + public long ConstructionTime; + public bool AstDataVerified; + + public LanguageData(Grammar grammar) { + Grammar = grammar; + GrammarData = new GrammarData(this); + ParserData = new ParserData(this); + ScannerData = new ScannerData(this); + ConstructAll(); + } + public void ConstructAll() { + var builder = new LanguageDataBuilder(this); + builder.Build(); + } + public bool CanParse() { + return ErrorLevel < GrammarErrorLevel.Error; + } + }//class +}//namespace diff --git a/Irony/Parsing/Data/ParserData.cs b/Irony/Parsing/Data/ParserData.cs new file mode 100644 index 0000000..4597484 --- /dev/null +++ b/Irony/Parsing/Data/ParserData.cs @@ -0,0 +1,124 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Irony.Parsing { + // ParserData is a container for all information used by CoreParser in input processing. + // ParserData is a field in LanguageData structure and is used by CoreParser when parsing intput. + // The state graph entry is InitialState state; the state graph encodes information usually contained + // in what is known in literature as transiton/goto tables. + // The graph is built from the language grammar by ParserDataBuilder. + using Irony.Parsing.Construction; + public class ParserData { + public readonly LanguageData Language; + public ParserState InitialState; //main initial state + public ParserStateTable InitialStates = new ParserStateTable(); // Lookup table: AugmRoot => InitialState + public readonly ParserStateList States = new ParserStateList(); + public ParserAction ErrorAction; + public ParserData(LanguageData language) { + Language = language; + } + } + + public class ParserState { + public readonly string Name; + public readonly ParserActionTable Actions = new ParserActionTable(); + //Defined for states with a single reduce item; Parser.GetAction returns this action if it is not null. + public ParserAction DefaultAction; + //Expected terms contains terminals is to be used in + //Parser-advise-to-Scanner facility would use it to filter current terminals when Scanner has more than one terminal for current char, + // it can ask Parser to filter the list using the ExpectedTerminals in current Parser state. + public readonly TerminalSet ExpectedTerminals = new TerminalSet(); + //Used for error reporting, we would use it to include list of expected terms in error message + // It is reduced compared to ExpectedTerms - some terms are "merged" into other non-terminals (with non-empty DisplayName) + // to make message shorter and cleaner. It is computed on-demand in CoreParser + public StringSet ReportedExpectedSet; + internal ParserStateData BuilderData; //transient, used only during automaton construction and may be cleared after that + + //Custom flags available for use by language/parser authors, to "mark" states in some way + // Irony reserves the highest order byte for internal use + public int CustomFlags; + + public ParserState(string name) { + Name = name; + } + public void ClearData() { + BuilderData = null; + } + public override string ToString() { + return Name; + } + public override int GetHashCode() { + return Name.GetHashCode(); + } + + public bool CustomFlagIsSet(int flag) { + return (CustomFlags & flag) != 0; + } + }//class + + public class ParserStateList : List { } + public class ParserStateSet : HashSet { } + public class ParserStateHash : Dictionary { } + public class ParserStateTable : Dictionary { } + + [Flags] + public enum ProductionFlags { + None = 0, + HasTerminals = 0x02, //contains terminal + IsError = 0x04, //contains Error terminal + IsEmpty = 0x08, + } + + public class Production { + public ProductionFlags Flags; + public readonly NonTerminal LValue; // left-side element + public readonly BnfTermList RValues = new BnfTermList(); //the right-side elements sequence + internal readonly Construction.LR0ItemList LR0Items = new Construction.LR0ItemList(); //LR0 items based on this production + + public Production(NonTerminal lvalue) { + LValue = lvalue; + }//constructor + + public string ToStringQuoted() { + return "'" + ToString() + "'"; + } + public override string ToString() { + return ProductionToString(this, -1); //no dot + } + public static string ProductionToString(Production production, int dotPosition) { + char dotChar = '\u00B7'; //dot in the middle of the line + StringBuilder bld = new StringBuilder(); + bld.Append(production.LValue.Name); + bld.Append(" -> "); + for (int i = 0; i < production.RValues.Count; i++) { + if (i == dotPosition) + bld.Append(dotChar); + bld.Append(production.RValues[i].Name); + bld.Append(" "); + }//for i + if (dotPosition == production.RValues.Count) + bld.Append(dotChar); + return bld.ToString(); + } + + }//Production class + + public class ProductionList : List { } + + +}//namespace diff --git a/Irony/Parsing/Data/ScannerData.cs b/Irony/Parsing/Data/ScannerData.cs new file mode 100644 index 0000000..d90d4c0 --- /dev/null +++ b/Irony/Parsing/Data/ScannerData.cs @@ -0,0 +1,36 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + public class TerminalLookupTable : Dictionary { } + + // ScannerData is a container for all detailed info needed by scanner to read input. + public class ScannerData { + public readonly LanguageData Language; + public readonly TerminalLookupTable TerminalsLookup = new TerminalLookupTable(); //hash table for fast terminal lookup by input char + public readonly TerminalList MultilineTerminals = new TerminalList(); + public TerminalList NoPrefixTerminals = new TerminalList(); //Terminals with no limited set of prefixes, copied from GrammarData + //hash table for fast lookup of non-grammar terminals by input char + public readonly TerminalLookupTable NonGrammarTerminalsLookup = new TerminalLookupTable(); + + public ScannerData(LanguageData language) { + Language = language; + } + }//class + +}//namespace diff --git a/Irony/Parsing/Grammar/BnfExpression.cs b/Irony/Parsing/Grammar/BnfExpression.cs new file mode 100644 index 0000000..4cefc80 --- /dev/null +++ b/Irony/Parsing/Grammar/BnfExpression.cs @@ -0,0 +1,73 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; + +namespace Irony.Parsing { + + //BNF expressions are represented as OR-list of Plus-lists of BNF terms + internal class BnfExpressionData : List { + public override string ToString() { + try { + string[] pipeArr = new string[this.Count]; + for (int i = 0; i < this.Count; i++) { + BnfTermList seq = this[i]; + string[] seqArr = new string[seq.Count]; + for (int j = 0; j < seq.Count; j++) + seqArr[j] = seq[j].ToString(); + pipeArr[i] = String.Join("+", seqArr); + } + return String.Join("|", pipeArr); + } catch(Exception e) { + return "(error: " + e.Message + ")"; + } + + } + } + + public class BnfExpression : BnfTerm { + + public BnfExpression(BnfTerm element): this() { + Data[0].Add(element); + } + public BnfExpression() : base(null) { + Data = new BnfExpressionData(); + Data.Add(new BnfTermList()); + } + + internal BnfExpressionData Data; + public override string ToString() { + return Data.ToString(); + } + + #region Implicit cast operators + public static implicit operator BnfExpression(string symbol) { + return new BnfExpression(Grammar.CurrentGrammar.ToTerm(symbol)); + } + //It seems better to define one method instead of the following two, with parameter of type BnfTerm - + // but that's not possible - it would be a conversion from base type of BnfExpression itself, which + // is not allowed in c# + public static implicit operator BnfExpression(Terminal term) { + return new BnfExpression(term); + } + public static implicit operator BnfExpression(NonTerminal nonTerminal) { + return new BnfExpression(nonTerminal); + } + #endregion + + + }//class + +}//namespace diff --git a/Irony/Parsing/Grammar/BnfTerm.cs b/Irony/Parsing/Grammar/BnfTerm.cs new file mode 100644 index 0000000..7b5acf9 --- /dev/null +++ b/Irony/Parsing/Grammar/BnfTerm.cs @@ -0,0 +1,237 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Reflection; + +using Irony.Ast; + +namespace Irony.Parsing { + [Flags] + public enum TermFlags { + None = 0, + IsOperator = 0x01, + IsOpenBrace = 0x02, + IsCloseBrace = 0x04, + IsBrace = IsOpenBrace | IsCloseBrace, + IsLiteral = 0x08, + + IsConstant = 0x10, + IsPunctuation = 0x20, + IsDelimiter = 0x40, + IsReservedWord = 0x080, + IsMemberSelect = 0x100, + InheritPrecedence = 0x200, // Signals that non-terminal must inherit precedence and assoc values from its children. + // Typically set for BinOp nonterminal (where BinOp.Rule = '+' | '-' | ...) + + IsNonScanner = 0x01000, // indicates that tokens for this terminal are NOT produced by scanner + IsNonGrammar = 0x02000, // if set, parser would eliminate the token from the input stream; terms in Grammar.NonGrammarTerminals have this flag set + IsTransient = 0x04000, // Transient non-terminal - should be replaced by it's child in the AST tree. + IsNotReported = 0x08000, // Exclude from expected terminals list on syntax error + + //calculated flags + IsNullable = 0x010000, + IsVisible = 0x020000, + IsKeyword = 0x040000, + IsMultiline = 0x100000, + //internal flags + IsList = 0x200000, + IsListContainer = 0x400000, + //Indicates not to create AST node; mainly to suppress warning message on some special nodes that AST node type is not specified + //Automatically set by MarkTransient method + NoAstNode = 0x800000, + //A flag to suppress automatic AST creation for child nodes in global AST construction. Will be used to supress full + // "compile" of method bodies in modules. The module might be large, but the running code might + // be actually using only a few methods or global members; so in this case it makes sense to "compile" only global/public + // declarations, including method headers but not full bodies. The body will be compiled on the first call. + // This makes even more sense when processing module imports. + AstDelayChildren = 0x1000000, + + } + + //Basic Backus-Naur Form element. Base class for Terminal, NonTerminal, BnfExpression, GrammarHint + public abstract class BnfTerm { + #region consructors + public BnfTerm(string name) : this(name, name) { } + public BnfTerm(string name, string errorAlias, Type nodeType) : this(name, errorAlias) { + AstConfig.NodeType = nodeType; + } + public BnfTerm(string name, string errorAlias, AstNodeCreator nodeCreator) : this(name, errorAlias) { + AstConfig.NodeCreator = nodeCreator; + } + public BnfTerm(string name, string errorAlias) { + Name = name; + ErrorAlias = errorAlias; + _hashCode = (_hashCounter++).GetHashCode(); + } + #endregion + + #region virtuals and overrides + public virtual void Init(GrammarData grammarData) { + GrammarData = grammarData; + } + + public virtual string GetParseNodeCaption(ParseTreeNode node) { + if (GrammarData != null) + return GrammarData.Grammar.GetParseNodeCaption(node); + else + return Name; + } + + public override string ToString() { + return Name; + } + + //Hash code - we use static counter to generate hash codes + private static int _hashCounter; + private int _hashCode; + public override int GetHashCode() { + return _hashCode; + } + #endregion + + public const int NoPrecedence = 0; + + #region properties: Name, DisplayName, Key, Options + public string Name; + + //ErrorAlias is used in error reporting, e.g. "Syntax error, expected ". + public string ErrorAlias; + public TermFlags Flags; + protected GrammarData GrammarData; + public int Precedence = NoPrecedence; + public Associativity Associativity = Associativity.Neutral; + + public Grammar Grammar { + get { return GrammarData.Grammar; } + } + public void SetFlag(TermFlags flag) { + SetFlag(flag, true); + } + public void SetFlag(TermFlags flag, bool value) { + if (value) + Flags |= flag; + else + Flags &= ~flag; + } + + #endregion + + #region events: Shifting + public event EventHandler Shifting; + public event EventHandler AstNodeCreated; //an event fired after AST node is created. + + protected internal void OnShifting(ParsingEventArgs args) { + if (Shifting != null) + Shifting(this, args); + } + + protected internal void OnAstNodeCreated(ParseTreeNode parseNode) { + if (this.AstNodeCreated == null || parseNode.AstNode == null) return; + AstNodeEventArgs args = new AstNodeEventArgs(parseNode); + AstNodeCreated(this, args); + } + + #endregion + + #region AST node creations: AstNodeType, AstNodeCreator, AstNodeCreated + //We autocreate AST config on first GET; + public AstNodeConfig AstConfig { + get { + if (_astConfig == null) + _astConfig = new Ast.AstNodeConfig(); + return _astConfig; + } + set {_astConfig = value; } + } AstNodeConfig _astConfig; + + public bool HasAstConfig() { + return _astConfig != null; + } + + #endregion + + + #region Kleene operator Q() + NonTerminal _q; + public BnfExpression Q() + { + if (_q != null) + return _q; + _q = new NonTerminal(this.Name + "?"); + _q.Rule = this | Grammar.CurrentGrammar.Empty; + return _q; + } + #endregion + + #region Operators: +, |, implicit + public static BnfExpression operator +(BnfTerm term1, BnfTerm term2) { + return Op_Plus(term1, term2); + } + public static BnfExpression operator +(BnfTerm term1, string symbol2) { + return Op_Plus(term1, Grammar.CurrentGrammar.ToTerm(symbol2)); + } + public static BnfExpression operator +( string symbol1, BnfTerm term2) { + return Op_Plus(Grammar.CurrentGrammar.ToTerm(symbol1), term2); + } + + //Alternative + public static BnfExpression operator |(BnfTerm term1, BnfTerm term2) { + return Op_Pipe(term1, term2); + } + public static BnfExpression operator |(BnfTerm term1, string symbol2) { + return Op_Pipe(term1, Grammar.CurrentGrammar.ToTerm(symbol2)); + } + public static BnfExpression operator |(string symbol1, BnfTerm term2) { + return Op_Pipe(Grammar.CurrentGrammar.ToTerm(symbol1), term2); + } + + //BNF operations implementation ----------------------- + // Plus/sequence + internal static BnfExpression Op_Plus(BnfTerm term1, BnfTerm term2) { + //Check term1 and see if we can use it as result, simply adding term2 as operand + BnfExpression expr1 = term1 as BnfExpression; + if (expr1 == null || expr1.Data.Count > 1) //either not expression at all, or Pipe-type expression (count > 1) + expr1 = new BnfExpression(term1); + expr1.Data[expr1.Data.Count - 1].Add(term2); + return expr1; + } + + //Pipe/Alternative + //New version proposed by the codeplex user bdaugherty + internal static BnfExpression Op_Pipe(BnfTerm term1, BnfTerm term2) { + BnfExpression expr1 = term1 as BnfExpression; + if (expr1 == null) + expr1 = new BnfExpression(term1); + BnfExpression expr2 = term2 as BnfExpression; + if (expr2 == null) + expr2 = new BnfExpression(term2); + expr1.Data.AddRange(expr2.Data); + return expr1; + } + + + #endregion + + + }//class + + public class BnfTermList : List { } + public class BnfTermSet : HashSet { } + + + +}//namespace + diff --git a/Irony/Parsing/Grammar/Grammar.cs b/Irony/Parsing/Grammar/Grammar.cs new file mode 100644 index 0000000..264607b --- /dev/null +++ b/Irony/Parsing/Grammar/Grammar.cs @@ -0,0 +1,513 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq.Expressions; +using System.Text; + +using Irony.Ast; + +namespace Irony.Parsing { + + + public class Grammar { + + #region properties + /// + /// Gets case sensitivity of the grammar. Read-only, true by default. + /// Can be set to false only through a parameter to grammar constructor. + /// + public readonly bool CaseSensitive; + + //List of chars that unambigously identify the start of new token. + //used in scanner error recovery, and in quick parse path in NumberLiterals, Identifiers + public string Delimiters = null; + + [Obsolete("Override Grammar.SkipWhitespace method instead.")] + // Not used anymore + public string WhitespaceChars = " \t\r\n\v"; + + public LanguageFlags LanguageFlags = LanguageFlags.Default; + + public TermReportGroupList TermReportGroups = new TermReportGroupList(); + + //Terminals not present in grammar expressions and not reachable from the Root + // (Comment terminal is usually one of them) + // Tokens produced by these terminals will be ignored by parser input. + public readonly TerminalSet NonGrammarTerminals = new TerminalSet(); + + /// + /// The main root entry for the grammar. + /// + public NonTerminal Root; + + /// + /// Alternative roots for parsing code snippets. + /// + public NonTerminalSet SnippetRoots = new NonTerminalSet(); + + public string GrammarComments; //shown in Grammar info tab + + public CultureInfo DefaultCulture = CultureInfo.InvariantCulture; + + //Console-related properties, initialized in grammar constructor + public string ConsoleTitle; + public string ConsoleGreeting; + public string ConsolePrompt; //default prompt + public string ConsolePromptMoreInput; //prompt to show when more input is expected + #endregion + + #region constructors + + public Grammar() : this(true) { } //case sensitive by default + + public Grammar(bool caseSensitive) { + _currentGrammar = this; + this.CaseSensitive = caseSensitive; + bool ignoreCase = !this.CaseSensitive; + var stringComparer = StringComparer.Create(System.Globalization.CultureInfo.InvariantCulture, ignoreCase); + KeyTerms = new KeyTermTable(stringComparer); + //Initialize console attributes + ConsoleTitle = Resources.MsgDefaultConsoleTitle; + ConsoleGreeting = string.Format(Resources.MsgDefaultConsoleGreeting, this.GetType().Name); + ConsolePrompt = ">"; + ConsolePromptMoreInput = "."; + } + #endregion + + #region Reserved words handling + //Reserved words handling + public void MarkReservedWords(params string[] reservedWords) { + foreach (var word in reservedWords) { + var wdTerm = ToTerm(word); + wdTerm.SetFlag(TermFlags.IsReservedWord); + } + } + #endregion + + #region Register/Mark methods + public void RegisterOperators(int precedence, params string[] opSymbols) { + RegisterOperators(precedence, Associativity.Left, opSymbols); + } + + public void RegisterOperators(int precedence, Associativity associativity, params string[] opSymbols) { + foreach (string op in opSymbols) { + KeyTerm opSymbol = ToTerm(op); + opSymbol.SetFlag(TermFlags.IsOperator); + opSymbol.Precedence = precedence; + opSymbol.Associativity = associativity; + } + }//method + + public void RegisterOperators(int precedence, params BnfTerm[] opTerms) { + RegisterOperators(precedence, Associativity.Left, opTerms); + } + public void RegisterOperators(int precedence, Associativity associativity, params BnfTerm[] opTerms) { + foreach (var term in opTerms) { + term.SetFlag(TermFlags.IsOperator); + term.Precedence = precedence; + term.Associativity = associativity; + } + } + + public void RegisterBracePair(string openBrace, string closeBrace) { + KeyTerm openS = ToTerm(openBrace); + KeyTerm closeS = ToTerm(closeBrace); + openS.SetFlag(TermFlags.IsOpenBrace); + openS.IsPairFor = closeS; + closeS.SetFlag(TermFlags.IsCloseBrace); + closeS.IsPairFor = openS; + } + + public void MarkPunctuation(params string[] symbols) { + foreach (string symbol in symbols) { + KeyTerm term = ToTerm(symbol); + term.SetFlag(TermFlags.IsPunctuation|TermFlags.NoAstNode); + } + } + + public void MarkPunctuation(params BnfTerm[] terms) { + foreach (BnfTerm term in terms) + term.SetFlag(TermFlags.IsPunctuation|TermFlags.NoAstNode); + } + + + public void MarkTransient(params NonTerminal[] nonTerminals) { + foreach (NonTerminal nt in nonTerminals) + nt.Flags |= TermFlags.IsTransient | TermFlags.NoAstNode; + } + //MemberSelect are symbols invoking member list dropdowns in editor; for ex: . (dot), :: + public void MarkMemberSelect(params string[] symbols) { + foreach (var symbol in symbols) + ToTerm(symbol).SetFlag(TermFlags.IsMemberSelect); + } + //Sets IsNotReported flag on terminals. As a result the terminal wouldn't appear in expected terminal list + // in syntax error messages + public void MarkNotReported(params BnfTerm[] terms) { + foreach (var term in terms) + term.SetFlag(TermFlags.IsNotReported); + } + public void MarkNotReported(params string[] symbols) { + foreach (var symbol in symbols) + ToTerm(symbol).SetFlag(TermFlags.IsNotReported); + } + + #endregion + + #region virtual methods: CreateTokenFilters, TryMatch + public virtual void CreateTokenFilters(LanguageData language, TokenFilterList filters) { + } + + //This method is called if Scanner fails to produce a token; it offers custom method a chance to produce the token + public virtual Token TryMatch(ParsingContext context, ISourceStream source) { + return null; + } + + //Gives a way to customize parse tree nodes captions in the tree view. + public virtual string GetParseNodeCaption(ParseTreeNode node) { + if (node.IsError) + return node.Term.Name + " (Syntax error)"; + if (node.Token != null) + return node.Token.ToString(); + if(node.Term == null) //special case for initial node pushed into the stack at parser start + return (node.State != null ? string.Empty : "(State " + node.State.Name + ")"); // Resources.LabelInitialState; + var ntTerm = node.Term as NonTerminal; + if(ntTerm != null && !string.IsNullOrEmpty(ntTerm.NodeCaptionTemplate)) + return ntTerm.GetNodeCaption(node); + return node.Term.Name; + } + + /// + /// Override this method to help scanner select a terminal to create token when there are more than one candidates + /// for an input char. context.CurrentTerminals contains candidate terminals; leave a single terminal in this list + /// as the one to use. + /// + public virtual void OnScannerSelectTerminal(ParsingContext context) { } + + /// Skips whitespace characters in the input stream. + /// Override this method if your language has non-standard whitespace characters. + /// Source stream. + public virtual void SkipWhitespace(ISourceStream source) { + while (!source.EOF()) { + switch (source.PreviewChar) { + case ' ': case '\t': + break; + case '\r': case '\n': case '\v': + if (UsesNewLine) return; //do not treat as whitespace if language is line-based + break; + default: + return; + }//switch + source.PreviewPosition++; + }//while + }//method + + /// Returns true if a character is whitespace or delimiter. Used in quick-scanning versions of some terminals. + /// The character to check. + /// True if a character is whitespace or delimiter; otherwise, false. + /// Does not have to be completely accurate, should recognize most common characters that are special chars by themselves + /// and may never be part of other multi-character tokens. + public virtual bool IsWhitespaceOrDelimiter(char ch) { + switch (ch) { + case ' ': case '\t': case '\r': case '\n': case '\v': //whitespaces + case '(': case ')': case ',': case ';': case '[': case ']': case '{': case '}': + return true; + default: + return false; + }//switch + }//method + + + //The method is called after GrammarData is constructed + public virtual void OnGrammarDataConstructed(LanguageData language) { + } + + public virtual void OnLanguageDataConstructed(LanguageData language) { + } + + + //Constructs the error message in situation when parser has no available action for current input. + // override this method if you want to change this message + public virtual string ConstructParserErrorMessage(ParsingContext context, StringSet expectedTerms) { + if(expectedTerms.Count > 0) + return string.Format(Resources.ErrSyntaxErrorExpected, expectedTerms.ToString(", ")); + else + return Resources.ErrParserUnexpectedInput; + } + + // Override this method to perform custom error processing + public virtual void ReportParseError(ParsingContext context) { + string error = null; + if (context.CurrentParserInput.Term == this.SyntaxError) + error = context.CurrentParserInput.Token.Value as string; //scanner error + else if (context.CurrentParserInput.Term == this.Indent) + error = Resources.ErrUnexpIndent; + else if (context.CurrentParserInput.Term == this.Eof && context.OpenBraces.Count > 0) { + if (context.OpenBraces.Count > 0) { + //report unclosed braces/parenthesis + var openBrace = context.OpenBraces.Peek(); + error = string.Format(Resources.ErrNoClosingBrace, openBrace.Text); + } else + error = Resources.ErrUnexpEof; + }else { + var expectedTerms = context.GetExpectedTermSet(); + error = ConstructParserErrorMessage(context, expectedTerms); + } + context.AddParserError(error); + }//method + #endregion + + #region MakePlusRule, MakeStarRule methods + public BnfExpression MakePlusRule(NonTerminal listNonTerminal, BnfTerm listMember) { + return MakeListRule(listNonTerminal, null, listMember); + } + public BnfExpression MakePlusRule(NonTerminal listNonTerminal, BnfTerm delimiter, BnfTerm listMember) { + return MakeListRule(listNonTerminal, delimiter, listMember); + } + public BnfExpression MakeStarRule(NonTerminal listNonTerminal, BnfTerm listMember) { + return MakeListRule(listNonTerminal, null, listMember, TermListOptions.StarList); + } + public BnfExpression MakeStarRule(NonTerminal listNonTerminal, BnfTerm delimiter, BnfTerm listMember) { + return MakeListRule(listNonTerminal, delimiter, listMember, TermListOptions.StarList); + } + + //Note: Here and in other make-list methods with delimiter. More logical would be the parameters order (list, listMember, delimiter=null). + // But for historical reasons it's the way it is, and I think it's too late to change and to reverse the order of delimiter and listMember. + // Too many existing grammars would be broken. The big trouble is that these two parameters are of the same type, so compiler would not + // detect that order had changed (if we change it) for existing grammars. The grammar would stop working at runtime, and it would + // require some effort to debug and find the cause of the problem. For these reasons, we leave it as is. + [Obsolete("Method overload is obsolete - use MakeListRule instead")] + public BnfExpression MakePlusRule(NonTerminal listNonTerminal, BnfTerm delimiter, BnfTerm listMember, TermListOptions options) { + return MakeListRule(listNonTerminal, delimiter, listMember, options); + } + + [Obsolete("Method overload is obsolete - use MakeListRule instead")] + public BnfExpression MakeStarRule(NonTerminal listNonTerminal, BnfTerm delimiter, BnfTerm listMember, TermListOptions options) { + return MakeListRule(listNonTerminal, delimiter, listMember, options | TermListOptions.StarList); + } + + protected BnfExpression MakeListRule(NonTerminal list, BnfTerm delimiter, BnfTerm listMember, TermListOptions options = TermListOptions.PlusList) { + //If it is a star-list (allows empty), then we first build plus-list + var isStarList = options.IsSet(TermListOptions.AllowEmpty); + NonTerminal plusList = isStarList ? new NonTerminal(listMember.Name + "+") : list; + //"list" is the real list for which we will construct expression - it is either extra plus-list or original listNonTerminal. + // In the latter case we will use it later to construct expression for listNonTerminal + plusList.Rule = plusList; // rule => list + if (delimiter != null) + plusList.Rule += delimiter; // rule => list + delim + if (options.IsSet(TermListOptions.AddPreferShiftHint)) + plusList.Rule += PreferShiftHere(); // rule => list + delim + PreferShiftHere() + plusList.Rule += listMember; // rule => list + delim + PreferShiftHere() + elem + plusList.Rule |= listMember; // rule => list + delim + PreferShiftHere() + elem | elem + //trailing delimiter + if (options.IsSet(TermListOptions.AllowTrailingDelimiter) & delimiter != null) + plusList.Rule |= list + delimiter; // => list + delim + PreferShiftHere() + elem | elem | list + delim + // set Rule value + plusList.SetFlag(TermFlags.IsList); + //If we do not use exra list - we're done, return list.Rule + if (plusList == list) + return list.Rule; + // Let's setup listNonTerminal.Rule using plus-list we just created + //If we are here, TermListOptions.AllowEmpty is set, so we have star-list + list.Rule = Empty | plusList; + plusList.SetFlag(TermFlags.NoAstNode); + list.SetFlag(TermFlags.IsListContainer); //indicates that real list is one level lower + return list.Rule; + }//method + #endregion + + #region Hint utilities + protected GrammarHint PreferShiftHere() { + return new PreferredActionHint(PreferredActionType.Shift); + } + protected GrammarHint ReduceHere() { + return new PreferredActionHint(PreferredActionType.Reduce); + } + protected TokenPreviewHint ReduceIf(string thisSymbol, params string[] comesBefore) { + return new TokenPreviewHint(PreferredActionType.Reduce, thisSymbol, comesBefore); + } + protected TokenPreviewHint ReduceIf(Terminal thisSymbol, params Terminal[] comesBefore) { + return new TokenPreviewHint(PreferredActionType.Reduce, thisSymbol, comesBefore); + } + protected TokenPreviewHint ShiftIf(string thisSymbol, params string[] comesBefore) { + return new TokenPreviewHint(PreferredActionType.Shift, thisSymbol, comesBefore); + } + protected TokenPreviewHint ShiftIf(Terminal thisSymbol, params Terminal[] comesBefore) { + return new TokenPreviewHint(PreferredActionType.Shift, thisSymbol, comesBefore); + } + protected GrammarHint ImplyPrecedenceHere(int precedence) { + return ImplyPrecedenceHere(precedence, Associativity.Left); + } + protected GrammarHint ImplyPrecedenceHere(int precedence, Associativity associativity) { + return new ImpliedPrecedenceHint(precedence, associativity); + } + protected CustomActionHint CustomActionHere(ExecuteActionMethod executeMethod, PreviewActionMethod previewMethod = null) { + return new CustomActionHint(executeMethod, previewMethod); + } + + #endregion + + #region Term report group methods + /// + /// Creates a terminal reporting group, so all terminals in the group will be reported as a single "alias" in syntex error messages like + /// "Syntax error, expected: [list of terms]" + /// + /// An alias for all terminals in the group. + /// Symbols to be included into the group. + protected void AddTermsReportGroup(string alias, params string[] symbols) { + TermReportGroups.Add(new TermReportGroup(alias, TermReportGroupType.Normal, SymbolsToTerms(symbols))); + } + /// + /// Creates a terminal reporting group, so all terminals in the group will be reported as a single "alias" in syntex error messages like + /// "Syntax error, expected: [list of terms]" + /// + /// An alias for all terminals in the group. + /// Terminals to be included into the group. + protected void AddTermsReportGroup(string alias, params Terminal[] terminals) { + TermReportGroups.Add(new TermReportGroup(alias, TermReportGroupType.Normal, terminals)); + } + /// + /// Adds symbols to a group with no-report type, so symbols will not be shown in expected lists in syntax error messages. + /// + /// Symbols to exclude. + protected void AddToNoReportGroup(params string[] symbols) { + TermReportGroups.Add(new TermReportGroup(string.Empty, TermReportGroupType.DoNotReport, SymbolsToTerms(symbols))); + } + /// + /// Adds symbols to a group with no-report type, so symbols will not be shown in expected lists in syntax error messages. + /// + /// Symbols to exclude. + protected void AddToNoReportGroup(params Terminal[] terminals) { + TermReportGroups.Add(new TermReportGroup(string.Empty, TermReportGroupType.DoNotReport, terminals)); + } + /// + /// Adds a group and an alias for all operator symbols used in the grammar. + /// + /// An alias for operator symbols. + protected void AddOperatorReportGroup(string alias) { + TermReportGroups.Add(new TermReportGroup(alias, TermReportGroupType.Operator, null)); //operators will be filled later + } + + private IEnumerable SymbolsToTerms(IEnumerable symbols) { + var termList = new TerminalList(); + foreach(var symbol in symbols) + termList.Add(ToTerm(symbol)); + return termList; + } + #endregion + + #region Standard terminals: EOF, Empty, NewLine, Indent, Dedent + // Empty object is used to identify optional element: + // term.Rule = term1 | Empty; + public readonly Terminal Empty = new Terminal("EMPTY"); + public readonly NewLineTerminal NewLine = new NewLineTerminal("LF"); + //set to true automatically by NewLine terminal; prevents treating new-line characters as whitespaces + public bool UsesNewLine; + // The following terminals are used in indent-sensitive languages like Python; + // they are not produced by scanner but are produced by CodeOutlineFilter after scanning + public readonly Terminal Indent = new Terminal("INDENT", TokenCategory.Outline, TermFlags.IsNonScanner); + public readonly Terminal Dedent = new Terminal("DEDENT", TokenCategory.Outline, TermFlags.IsNonScanner); + //End-of-Statement terminal - used in indentation-sensitive language to signal end-of-statement; + // it is not always synced with CRLF chars, and CodeOutlineFilter carefully produces Eos tokens + // (as well as Indent and Dedent) based on line/col information in incoming content tokens. + public readonly Terminal Eos = new Terminal("EOS", Resources.LabelEosLabel, TokenCategory.Outline, TermFlags.IsNonScanner); + // Identifies end of file + // Note: using Eof in grammar rules is optional. Parser automatically adds this symbol + // as a lookahead to Root non-terminal + public readonly Terminal Eof = new Terminal("EOF", TokenCategory.Outline); + + //Used as a "line-start" indicator + public readonly Terminal LineStartTerminal = new Terminal("LINE_START", TokenCategory.Outline); + + //Used for error tokens + public readonly Terminal SyntaxError = new Terminal("SYNTAX_ERROR", TokenCategory.Error, TermFlags.IsNonScanner); + + public NonTerminal NewLinePlus { + get { + if(_newLinePlus == null) { + _newLinePlus = new NonTerminal("LF+"); + //We do no use MakePlusRule method; we specify the rule explicitly to add PrefereShiftHere call - this solves some unintended shift-reduce conflicts + // when using NewLinePlus + _newLinePlus.Rule = NewLine | _newLinePlus + PreferShiftHere() + NewLine; + MarkPunctuation(_newLinePlus); + _newLinePlus.SetFlag(TermFlags.IsList); + } + return _newLinePlus; + } + } NonTerminal _newLinePlus; + + public NonTerminal NewLineStar { + get { + if(_newLineStar == null) { + _newLineStar = new NonTerminal("LF*"); + MarkPunctuation(_newLineStar); + _newLineStar.Rule = MakeStarRule(_newLineStar, NewLine); + } + return _newLineStar; + } + } NonTerminal _newLineStar; + + #endregion + + #region KeyTerms (keywords + special symbols) + public KeyTermTable KeyTerms; + + public KeyTerm ToTerm(string text) { + return ToTerm(text, text); + } + public KeyTerm ToTerm(string text, string name) { + KeyTerm term; + if (KeyTerms.TryGetValue(text, out term)) { + //update name if it was specified now and not before + if (string.IsNullOrEmpty(term.Name) && !string.IsNullOrEmpty(name)) + term.Name = name; + return term; + } + //create new term + if (!CaseSensitive) + text = text.ToLower(CultureInfo.InvariantCulture); + string.Intern(text); + term = new KeyTerm(text, name); + KeyTerms[text] = term; + return term; + } + + #endregion + + #region CurrentGrammar static field + //Static per-thread instance; Grammar constructor sets it to self (this). + // This field/property is used by operator overloads (which are static) to access Grammar's predefined terminals like Empty, + // and SymbolTerms dictionary to convert string literals to symbol terminals and add them to the SymbolTerms dictionary + [ThreadStatic] + private static Grammar _currentGrammar; + public static Grammar CurrentGrammar { + get { return _currentGrammar; } + } + internal static void ClearCurrentGrammar() { + _currentGrammar = null; + } + #endregion + + #region AST construction + public virtual void BuildAst(LanguageData language, ParseTree parseTree) { + if (!LanguageFlags.IsSet(LanguageFlags.CreateAst)) + return; + var astContext = new AstContext(language); + var astBuilder = new AstBuilder(astContext); + astBuilder.BuildAst(parseTree); + } + #endregion + }//class + +}//namespace diff --git a/Irony/Parsing/Grammar/GrammarEnums.cs b/Irony/Parsing/Grammar/GrammarEnums.cs new file mode 100644 index 0000000..6cdb761 --- /dev/null +++ b/Irony/Parsing/Grammar/GrammarEnums.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + [Flags] + public enum LanguageFlags { + None = 0, + + //Compilation options + //Be careful - use this flag ONLY if you use NewLine terminal in grammar explicitly! + // - it happens only in line-based languages like Basic. + NewLineBeforeEOF = 0x01, + //Emit LineStart token + EmitLineStartToken = 0x02, + DisableScannerParserLink = 0x04, //in grammars that define TokenFilters (like Python) this flag should be set + CreateAst = 0x08, //create AST nodes + + //Runtime + SupportsCommandLine = 0x0200, + TailRecursive = 0x0400, //Tail-recursive language - Scheme is one example + SupportsBigInt = 0x01000, + SupportsComplex = 0x02000, + SupportsRational = 0x04000, + + //Default value + Default = None, + } + + //Operator associativity types + public enum Associativity { + Left, + Right, + Neutral //honestly don't know what that means, but it is mentioned in literature + } + + //Used by Make-list-rule methods + [Flags] + public enum TermListOptions { + None = 0, + AllowEmpty = 0x01, + AllowTrailingDelimiter = 0x02, + + // In some cases this hint would help to resolve the conflicts that come up when you have two lists separated by a nullable term. + // This hint would resolve the conflict, telling the parser to include as many as possible elements in the first list, and the rest, + // if any, would go to the second list. By default, this flag is included in Star and Plus lists. + AddPreferShiftHint = 0x04, + //Combinations - use these + PlusList = AddPreferShiftHint, + StarList = AllowEmpty | AddPreferShiftHint, + } + +} diff --git a/Irony/Parsing/Grammar/GrammarError.cs b/Irony/Parsing/Grammar/GrammarError.cs new file mode 100644 index 0000000..dd2a1ed --- /dev/null +++ b/Irony/Parsing/Grammar/GrammarError.cs @@ -0,0 +1,73 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + public enum GrammarErrorLevel { + NoError, //used only for max error level when there are no errors + Info, + Warning, + Conflict, //shift-reduce or reduce-reduce conflict + Error, //severe grammar error, parser construction cannot continue + InternalError, //internal Irony error + } + + public class GrammarError { + public readonly GrammarErrorLevel Level; + public readonly string Message; + public readonly ParserState State; //can be null! + public GrammarError(GrammarErrorLevel level, ParserState state, string message) { + Level = level; + State = state; + Message = message; + } + public override string ToString() { + return Message + " (" + State + ")"; + } + }//class + + public class GrammarErrorList : List { + public void Add(GrammarErrorLevel level, ParserState state, string message, params object[] args) { + if (args != null && args.Length > 0) + message = String.Format(message, args); + base.Add(new GrammarError(level, state, message)); + } + public void AddAndThrow(GrammarErrorLevel level, ParserState state, string message, params object[] args) { + Add(level, state, message, args); + var error = this[this.Count - 1]; + var exc = new GrammarErrorException(error.Message, error); + throw exc; + } + public GrammarErrorLevel GetMaxLevel() { + var max = GrammarErrorLevel.NoError; + foreach (var err in this) + if (max < err.Level) + max = err.Level; + return max; + } + } + + //Used to cancel parser construction when fatal error is found + public class GrammarErrorException : Exception { + public readonly GrammarError Error; + public GrammarErrorException(string message, GrammarError error) : base(message) { + Error = error; + } + + }//class + + +} diff --git a/Irony/Parsing/Grammar/GrammarHint.cs b/Irony/Parsing/Grammar/GrammarHint.cs new file mode 100644 index 0000000..a34d9da --- /dev/null +++ b/Irony/Parsing/Grammar/GrammarHint.cs @@ -0,0 +1,49 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Parsing.Construction; + +namespace Irony.Parsing { + + public class GrammarHintList : List { } + + //Hints are additional instructions for parser added inside BNF expressions. + // Hint refers to specific position inside the expression (production), so hints are associated with LR0Item object + // One example is a PreferredActionHint produced by the Grammar.PreferShiftHere() method. It tells parser to perform + // shift in case of a shift/reduce conflict. It is in fact the default action of LALR parser, so the hint simply suppresses the error + // message about the shift/reduce conflict in the grammar. + public abstract class GrammarHint : BnfTerm { + public GrammarHint() : base("hint") { } + + /// Gives a chance to a custom code in hint to interfere in parser automaton construction. + /// The LanguageData instance. + /// The LRItem that "owns" the hint. + /// + /// The most common purpose of this method (it's overrides) is to resolve the conflicts + /// by adding specific actions into State.Actions dictionary. + /// The owner parameter represents the position in the grammar expression where the hint + /// is found. The parser state is available through owner.State property. + /// + public virtual void Apply(LanguageData language, LRItem owner) { + // owner.State -- the parser state + // owner.State.BuilderData.Conflicts -- as set of conflict terminals + // owner.State.Actions -- a dictionary of actions in the current state. + } + } //class + + +} diff --git a/Irony/Parsing/Grammar/ICanRunSample.cs b/Irony/Parsing/Grammar/ICanRunSample.cs new file mode 100644 index 0000000..326deed --- /dev/null +++ b/Irony/Parsing/Grammar/ICanRunSample.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + // Should be implemented by Grammar class to be able to run samples in Grammar Explorer. + public interface ICanRunSample { + string RunSample(RunSampleArgs args); + } + + public class RunSampleArgs { + public LanguageData Language; + public string Sample; + public ParseTree ParsedSample; + public RunSampleArgs(LanguageData language, string sample, ParseTree parsedSample) { + Language = language; + Sample = sample; + ParsedSample = parsedSample; + } + } + +} diff --git a/Irony/Parsing/Grammar/LanguageAttribute.cs b/Irony/Parsing/Grammar/LanguageAttribute.cs new file mode 100644 index 0000000..97014a3 --- /dev/null +++ b/Irony/Parsing/Grammar/LanguageAttribute.cs @@ -0,0 +1,53 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + [AttributeUsage(AttributeTargets.Class)] + public class LanguageAttribute : Attribute { + public LanguageAttribute() : this(null) { } + public LanguageAttribute(string languageName) : this(languageName, "1.0", string.Empty) { } + + public LanguageAttribute(string languageName, string version, string description) { + _languageName = languageName; + _version = version; + _description = description; + } + + public string LanguageName { + get { return _languageName; } + } string _languageName; + + public string Version { + get { return _version; } + } string _version; + + public string Description { + get { return _description; } + } string _description; + + public static LanguageAttribute GetValue(Type grammarClass) { + object[] attrs = grammarClass.GetCustomAttributes(typeof(LanguageAttribute), true); + if (attrs != null && attrs.Length > 0) { + LanguageAttribute la = attrs[0] as LanguageAttribute; + return la; + } + return null; + } + + }//class +}//namespace diff --git a/Irony/Parsing/Grammar/NonTerminal.cs b/Irony/Parsing/Grammar/NonTerminal.cs new file mode 100644 index 0000000..d893211 --- /dev/null +++ b/Irony/Parsing/Grammar/NonTerminal.cs @@ -0,0 +1,139 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using Irony.Ast; + +namespace Irony.Parsing { + + internal class IntList : List { } + + public class NonTerminal : BnfTerm { + + #region constructors + public NonTerminal(string name) : base(name, null) { } //by default display name is null + public NonTerminal(string name, string errorAlias) : base(name, errorAlias) { } + public NonTerminal(string name, string errorAlias, Type nodeType) : base(name, errorAlias, nodeType ) { } + public NonTerminal(string name, string errorAlias, AstNodeCreator nodeCreator) : base(name, errorAlias, nodeCreator) {} + public NonTerminal(string name, Type nodeType) : base(name, null, nodeType) { } + public NonTerminal(string name, AstNodeCreator nodeCreator) : base(name, null, nodeCreator) { } + public NonTerminal(string name, BnfExpression expression) + : this(name) { + Rule = expression; + } + #endregion + + #region properties/fields: Rule, ErrorRule + + public BnfExpression Rule; + //Separate property for specifying error expressions. This allows putting all such expressions in a separate section + // in grammar for all non-terminals. However you can still put error expressions in the main Rule property, just like + // in YACC + public BnfExpression ErrorRule; + + //A template for representing ParseTreeNode in the parse tree. Can contain '#{i}' fragments referencing + // child nodes by index + public string NodeCaptionTemplate; + //Converted template with index list + private string _convertedTemplate; + private IntList _captionParameters; + + // Productions are used internally by Parser builder + internal ProductionList Productions = new ProductionList(); + #endregion + + #region Events: Reduced + //Note that Reduced event may be called more than once for a List node + public event EventHandler Reduced; + internal void OnReduced(ParsingContext context, Production reducedProduction, ParseTreeNode resultNode) { + if (Reduced != null) + Reduced(this, new ReducedEventArgs(context, reducedProduction, resultNode)); + } + #endregion + + #region overrides: ToString, Init + public override string ToString() { + return Name; + } + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + if (!string.IsNullOrEmpty(NodeCaptionTemplate)) + ConvertNodeCaptionTemplate(); + } + #endregion + + // Contributed by Alexey Yakovlev (yallie) + #region Grammar hints + // Adds a hint at the end of all productions + public void AddHintToAll(GrammarHint hint) { + if (this.Rule == null) + throw new Exception("Rule property must be set on non-terminal before calling AddHintToAll."); + foreach (var plusList in this.Rule.Data) + plusList.Add(hint); + } + + #endregion + + #region NodeCaptionTemplate utilities + //We replace original tag '#{i}' (where i is the index of the child node to put here) + // with the tag '{k}', where k is the number of the parameter. So after conversion the template can + // be used in string.Format() call, with parameters set to child nodes captions + private void ConvertNodeCaptionTemplate() { + _captionParameters = new IntList(); + _convertedTemplate = NodeCaptionTemplate; + var index = 0; + while(index < 100) { + var strParam = "#{" + index + "}"; + if(_convertedTemplate.Contains(strParam)) { + _convertedTemplate = _convertedTemplate.Replace(strParam, "{" + _captionParameters.Count + "}"); + _captionParameters.Add(index); + } + if (!_convertedTemplate.Contains("#{")) return; + index++; + }//while + }//method + + public string GetNodeCaption(ParseTreeNode node) { + var paramValues = new string[_captionParameters.Count]; + for(int i = 0; i < _captionParameters.Count; i++) { + var childIndex = _captionParameters[i]; + if(childIndex < node.ChildNodes.Count) { + var child = node.ChildNodes[childIndex]; + //if child is a token, then child.ToString returns token.ToString which contains Value + Term; + // in this case we prefer to have Value only + paramValues[i] = (child.Token != null? child.Token.ValueString : child.ToString()); + } + } + var result = string.Format(_convertedTemplate, paramValues); + return result; + } + #endregion + + }//class + + public class NonTerminalList : List { + public override string ToString() { + return string.Join(" ", this); + } + } + + public class NonTerminalSet : HashSet { + public override string ToString() { + return string.Join(" ", this); + } + } + + +}//namespace diff --git a/Irony/Parsing/Grammar/TermReportGroups.cs b/Irony/Parsing/Grammar/TermReportGroups.cs new file mode 100644 index 0000000..0579bf4 --- /dev/null +++ b/Irony/Parsing/Grammar/TermReportGroups.cs @@ -0,0 +1,50 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + //Terminal report group is a facility for improving syntax error messages. + // Irony parser/scanner reports an error like "Syntax error, invalid character. Expected: ." + // The is a list of all terminals (symbols) that are expected in current position. + // This list might quite long and quite difficult to look through. The solution is to provide Group names for + // groups of terminals - these are groups of type Normal. + // Some terminals might be excluded from showing in expected list by including them into group of type DoNotReport. + // Finally, Operator group allows you to specify group name for all operator symbols without listing operators - + // Irony will collect all operator symbols registered with RegisterOperator method automatically. + + public enum TermReportGroupType { + Normal, + DoNotReport, + Operator + } + public class TermReportGroup { + public string Alias; + public TermReportGroupType GroupType; + public TerminalSet Terminals = new TerminalSet(); + + public TermReportGroup(string alias, TermReportGroupType groupType, IEnumerable terminals) { + Alias = alias; + GroupType = groupType; + if (terminals != null) + Terminals.UnionWith(terminals); + } + + }//class + + public class TermReportGroupList : List { } + +}//namespace diff --git a/Irony/Parsing/Parser/ParseTree.cs b/Irony/Parsing/Parser/ParseTree.cs new file mode 100644 index 0000000..8f62058 --- /dev/null +++ b/Irony/Parsing/Parser/ParseTree.cs @@ -0,0 +1,141 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + /* + A node for a parse tree (concrete syntax tree) - an initial syntax representation produced by parser. + It contains all syntax elements of the input text, each element represented by a generic node ParseTreeNode. + The parse tree is converted into abstract syntax tree (AST) which contains custom nodes. The conversion might + happen on-the-fly: as parser creates the parse tree nodes it can create the AST nodes and puts them into AstNode field. + Alternatively it might happen as a separate step, after completing the parse tree. + AST node might optinally implement IAstNodeInit interface, so Irony parser can initialize the node providing it + with all relevant information. + The ParseTreeNode also works as a stack element in the parser stack, so it has the State property to carry + the pushed parser state while it is in the stack. + */ + public class ParseTreeNode { + public object AstNode; + public Token Token; + public BnfTerm Term; + public int Precedence; + public Associativity Associativity; + public SourceSpan Span; + //Making ChildNodes property (not field) following request by Matt K, Bill H + public ParseTreeNodeList ChildNodes {get; private set;} + public bool IsError; + internal ParserState State; //used by parser to store current state when node is pushed into the parser stack + public object Tag; //for use by custom parsers, Irony does not use it + public TokenList Comments; //Comments preceding this node + + private ParseTreeNode(){ + ChildNodes = new ParseTreeNodeList(); + } + + public ParseTreeNode(Token token) : this() { + Token = token; + Term = token.Terminal; + Precedence = Term.Precedence; + Associativity = token.Terminal.Associativity; + Span = new SourceSpan(token.Location, token.Length); + IsError = token.IsError(); + } + + public ParseTreeNode(ParserState initialState) : this() { + State = initialState; + } + + public ParseTreeNode(NonTerminal term, SourceSpan span) : this(){ + Term = term; + Span = span; + } + + public override string ToString() { + if (Term == null) + return "(S0)"; //initial state node + else + return Term.GetParseNodeCaption(this); + }//method + + + public string FindTokenAndGetText() { + var tkn = FindToken(); + return tkn == null ? null : tkn.Text; + } + public Token FindToken() { + return FindFirstChildTokenRec(this); + } + private static Token FindFirstChildTokenRec(ParseTreeNode node) { + if (node.Token != null) return node.Token; + foreach (var child in node.ChildNodes) { + var tkn = FindFirstChildTokenRec(child); + if (tkn != null) return tkn; + } + return null; + } + + /// Returns true if the node is punctuation or it is transient with empty child list. + /// True if parser can safely ignore this node. + public bool IsPunctuationOrEmptyTransient() { + if (Term.Flags.IsSet(TermFlags.IsPunctuation)) + return true; + if (Term.Flags.IsSet(TermFlags.IsTransient) && ChildNodes.Count == 0) + return true; + return false; + } + + public bool IsOperator() { + return Term.Flags.IsSet(TermFlags.IsOperator); + } + + }//class + + public class ParseTreeNodeList : List { } + + public enum ParseTreeStatus { + Parsing, + Partial, + Parsed, + Error, + } + + public class ParseTree { + public ParseTreeStatus Status {get; internal set;} + public readonly string SourceText; + public readonly string FileName; + public readonly TokenList Tokens = new TokenList(); + public readonly TokenList OpenBraces = new TokenList(); + public ParseTreeNode Root; + public readonly LogMessageList ParserMessages = new LogMessageList(); + public long ParseTimeMilliseconds; + public object Tag; //custom data object, use it anyway you want + + public ParseTree(string sourceText, string fileName) { + SourceText = sourceText; + FileName = fileName; + Status = ParseTreeStatus.Parsing; + } + + public bool HasErrors() { + if (ParserMessages.Count == 0) return false; + foreach (var err in ParserMessages) + if (err.Level == ErrorLevel.Error) return true; + return false; + }//method + + }//class + +} diff --git a/Irony/Parsing/Parser/ParseTreeExtensions.cs b/Irony/Parsing/Parser/ParseTreeExtensions.cs new file mode 100644 index 0000000..a9e7042 --- /dev/null +++ b/Irony/Parsing/Parser/ParseTreeExtensions.cs @@ -0,0 +1,66 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Xml; + +namespace Irony.Parsing { +#if !SILVERLIGHT + public static class ParseTreeExtensions { + + public static string ToXml(this ParseTree parseTree) { + if (parseTree == null || parseTree.Root == null) return string.Empty; + var xdoc = ToXmlDocument(parseTree); + StringWriter sw = new StringWriter(); + XmlTextWriter xw = new XmlTextWriter(sw); + xw.Formatting = Formatting.Indented; + xdoc.WriteTo(xw); + xw.Flush(); + return sw.ToString(); + } + + public static XmlDocument ToXmlDocument(this ParseTree parseTree) { + var xdoc = new XmlDocument(); + if (parseTree == null || parseTree.Root == null) return xdoc; + var xTree = xdoc.CreateElement("ParseTree"); + xdoc.AppendChild(xTree); + var xRoot = parseTree.Root.ToXmlElement(xdoc); + xTree.AppendChild(xRoot); + return xdoc; + } + + public static XmlElement ToXmlElement(this ParseTreeNode node, XmlDocument ownerDocument) { + var xElem = ownerDocument.CreateElement("Node"); + xElem.SetAttribute("Term", node.Term.Name); + var term = node.Term; + if (term.HasAstConfig() && term.AstConfig.NodeType != null) + xElem.SetAttribute("AstNodeType", term.AstConfig.NodeType.Name); + if (node.Token != null) { + xElem.SetAttribute("Terminal", node.Term.GetType().Name); + //xElem.SetAttribute("Text", node.Token.Text); + if (node.Token.Value != null) + xElem.SetAttribute("Value", node.Token.Value.ToString()); + } else + foreach (var child in node.ChildNodes) { + var xChild = child.ToXmlElement(ownerDocument); + xElem.AppendChild(xChild); + } + return xElem; + }//method + + }//class +#endif +}//namespace diff --git a/Irony/Parsing/Parser/Parser.cs b/Irony/Parsing/Parser/Parser.cs new file mode 100644 index 0000000..90487a4 --- /dev/null +++ b/Irony/Parsing/Parser/Parser.cs @@ -0,0 +1,254 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + //Parser class represents combination of scanner and LALR parser (CoreParser) + public class Parser { + public readonly LanguageData Language; + public readonly ParserData Data; + private Grammar _grammar; + //public readonly CoreParser CoreParser; + public readonly Scanner Scanner; + public ParsingContext Context { get; internal set; } + public readonly NonTerminal Root; + // Either language root or initial state for parsing snippets - like Ruby's expressions in strings : "result= #{x+y}" + internal readonly ParserState InitialState; + + public Parser(Grammar grammar) : this (new LanguageData(grammar)) { } + public Parser(LanguageData language) : this(language, null) {} + public Parser(LanguageData language, NonTerminal root) { + Language = language; + Data = Language.ParserData; + _grammar = Language.Grammar; + Context = new ParsingContext(this); + Scanner = new Scanner(this); + Root = root; + if(Root == null) { + Root = Language.Grammar.Root; + InitialState = Language.ParserData.InitialState; + } else { + if(Root != Language.Grammar.Root && !Language.Grammar.SnippetRoots.Contains(Root)) + throw new Exception(string.Format(Resources.ErrRootNotRegistered, root.Name)); + InitialState = Language.ParserData.InitialStates[Root]; + } + } + + internal void Reset() { + Context.Reset(); + Scanner.Reset(); + } + + + public ParseTree Parse(string sourceText) { + return Parse(sourceText, "Source"); + } + + public ParseTree Parse(string sourceText, string fileName) { + SourceLocation loc = default(SourceLocation); + Reset(); +/* if (Context.Status == ParserStatus.AcceptedPartial) { + var oldLoc = Context.Source.Location; + loc = new SourceLocation(oldLoc.Position, oldLoc.Line + 1, 0); + } else { + }*/ + Context.Source = new SourceStream(sourceText, this.Language.Grammar.CaseSensitive, Context.TabWidth, loc); + Context.CurrentParseTree = new ParseTree(sourceText, fileName); + Context.Status = ParserStatus.Parsing; + var sw = new Stopwatch(); + sw.Start(); + ParseAll(); + //Set Parse status + var parseTree = Context.CurrentParseTree; + bool hasErrors = parseTree.HasErrors(); + if (hasErrors) + parseTree.Status = ParseTreeStatus.Error; + else if (Context.Status == ParserStatus.AcceptedPartial) + parseTree.Status = ParseTreeStatus.Partial; + else + parseTree.Status = ParseTreeStatus.Parsed; + //Build AST if no errors and AST flag is set + bool createAst = _grammar.LanguageFlags.IsSet(LanguageFlags.CreateAst); + if (createAst && !hasErrors) + Language.Grammar.BuildAst(Language, parseTree); + //Done; record the time + sw.Stop(); + parseTree.ParseTimeMilliseconds = sw.ElapsedMilliseconds; + if (parseTree.ParserMessages.Count > 0) + parseTree.ParserMessages.Sort(LogMessageList.ByLocation); + return parseTree; + } + + private void ParseAll() { + //main loop + Context.Status = ParserStatus.Parsing; + while (Context.Status == ParserStatus.Parsing) { + ExecuteNextAction(); + } + }//ParseAll method + + public ParseTree ScanOnly(string sourceText, string fileName) { + Context.CurrentParseTree = new ParseTree(sourceText, fileName); + Context.Source = new SourceStream(sourceText, Language.Grammar.CaseSensitive, Context.TabWidth); + while (true) { + var token = Scanner.GetToken(); + if (token == null || token.Terminal == Language.Grammar.Eof) break; + } + return Context.CurrentParseTree; + } + + #region Parser Action execution + private void ExecuteNextAction() { + //Read input only if DefaultReduceAction is null - in this case the state does not contain ExpectedSet, + // so parser cannot assist scanner when it needs to select terminal and therefore can fail + if (Context.CurrentParserInput == null && Context.CurrentParserState.DefaultAction == null) + ReadInput(); + //Check scanner error + if (Context.CurrentParserInput != null && Context.CurrentParserInput.IsError) { + RecoverFromError(); + return; + } + //Try getting action + var action = GetNextAction(); + if (action == null) { + if (CheckPartialInputCompleted()) return; + RecoverFromError(); + return; + } + //We have action. Write trace and execute it + if (Context.TracingEnabled) + Context.AddTrace(action.ToString()); + action.Execute(Context); + } + + internal ParserAction GetNextAction() { + var currState = Context.CurrentParserState; + var currInput = Context.CurrentParserInput; + + if (currState.DefaultAction != null) + return currState.DefaultAction; + ParserAction action; + //First try as keyterm/key symbol; for example if token text = "while", then first try it as a keyword "while"; + // if this does not work, try as an identifier that happens to match a keyword but is in fact identifier + Token inputToken = currInput.Token; + if (inputToken != null && inputToken.KeyTerm != null) { + var keyTerm = inputToken.KeyTerm; + if (currState.Actions.TryGetValue(keyTerm, out action)) { + #region comments + // Ok, we found match as a key term (keyword or special symbol) + // Backpatch the token's term. For example in most cases keywords would be recognized as Identifiers by Scanner. + // Identifier would also check with SymbolTerms table and set AsSymbol field to SymbolTerminal if there exist + // one for token content. So we first find action by Symbol if there is one; if we find action, then we + // patch token's main terminal to AsSymbol value. This is important for recognizing keywords (for colorizing), + // and for operator precedence algorithm to work when grammar uses operators like "AND", "OR", etc. + //TODO: This might be not quite correct action, and we can run into trouble with some languages that have keywords that + // are not reserved words. But proper implementation would require substantial addition to parser code: + // when running into errors, we need to check the stack for places where we made this "interpret as Symbol" + // decision, roll back the stack and try to reinterpret as identifier + #endregion + inputToken.SetTerminal(keyTerm); + currInput.Term = keyTerm; + currInput.Precedence = keyTerm.Precedence; + currInput.Associativity = keyTerm.Associativity; + return action; + } + } + //Try to get by main Terminal, only if it is not the same as symbol + if (currState.Actions.TryGetValue(currInput.Term, out action)) + return action; + //If input is EOF and NewLineBeforeEof flag is set, try using NewLine to find action + if (currInput.Term == _grammar.Eof && _grammar.LanguageFlags.IsSet(LanguageFlags.NewLineBeforeEOF) && + currState.Actions.TryGetValue(_grammar.NewLine, out action)) { + //There's no action for EOF but there's action for NewLine. Let's add newLine token as input, just in case + // action code wants to check input - it should see NewLine. + var newLineToken = new Token(_grammar.NewLine, currInput.Token.Location, "\r\n", null); + var newLineNode = new ParseTreeNode(newLineToken); + Context.CurrentParserInput = newLineNode; + return action; + }//if + return null; + } + + #endregion + + #region reading input + public void ReadInput() { + Token token; + Terminal term; + //Get token from scanner while skipping all comment tokens (but accumulating them in comment block) + do { + token = Scanner.GetToken(); + term = token.Terminal; + if (term.Category == TokenCategory.Comment) + Context.CurrentCommentTokens.Add(token); + } while (term.Flags.IsSet(TermFlags.IsNonGrammar) && term != _grammar.Eof); + //Check brace token + if (term.Flags.IsSet(TermFlags.IsBrace) && !CheckBraceToken(token)) + token = new Token(_grammar.SyntaxError, token.Location, token.Text, + string.Format(Resources.ErrUnmatchedCloseBrace, token.Text)); + //Create parser input node + Context.CurrentParserInput = new ParseTreeNode(token); + //attach comments if any accumulated to content token + if (token.Terminal.Category == TokenCategory.Content) { + Context.CurrentParserInput.Comments = Context.CurrentCommentTokens; + Context.CurrentCommentTokens = new TokenList(); + } + //Fire event on Terminal + token.Terminal.OnParserInputPreview(Context); + } + #endregion + + #region Error Recovery + public void RecoverFromError() { + this.Data.ErrorAction.Execute(Context); + } + #endregion + + #region Utilities + private bool CheckPartialInputCompleted() { + bool partialCompleted = (Context.Mode == ParseMode.CommandLine && Context.CurrentParserInput.Term == _grammar.Eof); + if (!partialCompleted) return false; + Context.Status = ParserStatus.AcceptedPartial; + // clean up EOF in input so we can continue parsing next line + Context.CurrentParserInput = null; + return true; + } + + // We assume here that the token is a brace (opening or closing) + private bool CheckBraceToken(Token token) { + if (token.Terminal.Flags.IsSet(TermFlags.IsOpenBrace)) { + Context.OpenBraces.Push(token); + return true; + } + //it is closing brace; check if we have opening brace in the stack + var braces = Context.OpenBraces; + var match = (braces.Count > 0 && braces.Peek().Terminal.IsPairFor == token.Terminal); + if (!match) return false; + //Link both tokens, pop the stack and return true + var openingBrace = braces.Pop(); + openingBrace.OtherBrace = token; + token.OtherBrace = openingBrace; + return true; + } + #endregion + + + + + }//class +}//namespace diff --git a/Irony/Parsing/Parser/ParserActions/AcceptParserAction.cs b/Irony/Parsing/Parser/ParserActions/AcceptParserAction.cs new file mode 100644 index 0000000..642683a --- /dev/null +++ b/Irony/Parsing/Parser/ParserActions/AcceptParserAction.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + public class AcceptParserAction: ParserAction { + + public override void Execute(ParsingContext context) { + context.CurrentParseTree.Root = context.ParserStack.Pop(); //Pop root + context.Status = ParserStatus.Accepted; + } + + public override string ToString() { + return Resources.LabelActionAccept; + } + }//class +} diff --git a/Irony/Parsing/Parser/ParserActions/ErrorRecoveryParserAction.cs b/Irony/Parsing/Parser/ParserActions/ErrorRecoveryParserAction.cs new file mode 100644 index 0000000..76ab491 --- /dev/null +++ b/Irony/Parsing/Parser/ParserActions/ErrorRecoveryParserAction.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + //TODO: Improve recovery by adding automatic injection of missing tokens. + // Make sure we ALWAYS have output parse tree, even if it is messed up + public class ErrorRecoveryParserAction : ParserAction { + + public override void Execute(ParsingContext context) { + context.Status = ParserStatus.Error; + var grammar = context.Language.Grammar; + grammar.ReportParseError(context); + // Do not recover if we're already at EOF, or if we're in command line mode + if (context.CurrentParserInput.Term == grammar.Eof || context.Mode == ParseMode.CommandLine) + return; + //Try to recover from error + context.Status = ParserStatus.Recovering; + context.AddTrace(Resources.MsgTraceRecovering); // *** RECOVERING - searching for state with error shift *** + var recovered = TryRecoverFromError(context); + if (recovered) { + context.AddTrace(Resources.MsgTraceRecoverSuccess); //add new trace entry + context.Status = ParserStatus.Parsing; + } else { + context.AddTrace(Resources.MsgTraceRecoverFailed); + context.Status = ParserStatus.Error; + } + } + + protected bool TryRecoverFromError(ParsingContext context) { + var grammar = context.Language.Grammar; + var parser = context.Parser; + //1. We need to find a state in the stack that has a shift item based on error production (with error token), + // and error terminal is current. This state would have a shift action on error token. + ParserAction errorShiftAction = FindErrorShiftActionInStack(context); + if (errorShiftAction == null) return false; //we failed to recover + context.AddTrace(Resources.MsgTraceRecoverFoundState, context.CurrentParserState); + //2. Shift error token - execute shift action + context.AddTrace(Resources.MsgTraceRecoverShiftError, errorShiftAction); + errorShiftAction.Execute(context); + //4. Now we need to go along error production until the end, shifting tokens that CAN be shifted and ignoring others. + // We shift until we can reduce + context.AddTrace(Resources.MsgTraceRecoverShiftTillEnd); + while (true) { + if (context.CurrentParserInput == null) + parser.ReadInput(); + if (context.CurrentParserInput.Term == grammar.Eof) + return false; + //Check if we can reduce + var nextAction = parser.GetNextAction(); + if (nextAction == null) { + parser.ReadInput(); + continue; + } + if (nextAction is ReduceParserAction) { + //We are reducing a fragment containing error - this is the end of recovery + //Clear all input token queues and buffered input, reset location back to input position token queues; + context.SetSourceLocation(context.CurrentParserInput.Span.Location); + + //Reduce error production - it creates parent non-terminal that "hides" error inside + context.AddTrace(Resources.MsgTraceRecoverReducing); + context.AddTrace(Resources.MsgTraceRecoverAction, nextAction); + nextAction.Execute(context); //execute reduce + return true; //we recovered + } + // If it is not reduce, simply execute it (it is most likely shift) + context.AddTrace(Resources.MsgTraceRecoverAction, nextAction); + nextAction.Execute(context); //shift input token + } + }//method + + private ParserAction FindErrorShiftActionInStack(ParsingContext context) { + var grammar = context.Language.Grammar; + while (context.ParserStack.Count >= 1) { + ParserAction errorShiftAction; + if (context.CurrentParserState.Actions.TryGetValue(grammar.SyntaxError, out errorShiftAction) + && errorShiftAction is ShiftParserAction) + return errorShiftAction; + //pop next state from stack + if (context.ParserStack.Count == 1) + return null; //don't pop the initial state + context.ParserStack.Pop(); + context.CurrentParserState = context.ParserStack.Top.State; + } + return null; + } + + }//class +}//ns diff --git a/Irony/Parsing/Parser/ParserActions/ReduceParserActions.cs b/Irony/Parsing/Parser/ParserActions/ReduceParserActions.cs new file mode 100644 index 0000000..2b5b853 --- /dev/null +++ b/Irony/Parsing/Parser/ParserActions/ReduceParserActions.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + /// Base class for more specific reduce actions. + public class ReduceParserAction: ParserAction { + public readonly Production Production; + + public ReduceParserAction(Production production) { + Production = production; + } + public override string ToString() { + return string.Format(Resources.LabelActionReduce, Production.ToStringQuoted()); + } + + /// Factory method for creating a proper type of reduce parser action. + /// A Production to reduce. + /// Reduce action. + public static ReduceParserAction Create(Production production) { + var nonTerm = production.LValue; + //List builder (non-empty production for list non-terminal) is a special case + var isList = nonTerm.Flags.IsSet(TermFlags.IsList); + var isListBuilderProduction = isList && production.RValues.Count > 0 && production.RValues[0] == production.LValue; + if (isListBuilderProduction) + return new ReduceListBuilderParserAction(production); + else if (nonTerm.Flags.IsSet(TermFlags.IsListContainer)) + return new ReduceListContainerParserAction(production); + else if (nonTerm.Flags.IsSet(TermFlags.IsTransient)) + return new ReduceTransientParserAction(production); + else + return new ReduceParserAction(production); + } + + public override void Execute(ParsingContext context) { + var savedParserInput = context.CurrentParserInput; + context.CurrentParserInput = GetResultNode(context); + CompleteReduce(context); + context.CurrentParserInput = savedParserInput; + } + + protected virtual ParseTreeNode GetResultNode(ParsingContext context) { + var childCount = Production.RValues.Count; + int firstChildIndex = context.ParserStack.Count - childCount; + var span = context.ComputeStackRangeSpan(childCount); + var newNode = new ParseTreeNode(Production.LValue, span); + for (int i = 0; i < childCount; i++) { + var childNode = context.ParserStack[firstChildIndex + i]; + if (childNode.IsPunctuationOrEmptyTransient()) continue; //skip punctuation or empty transient nodes + newNode.ChildNodes.Add(childNode); + }//for i + return newNode; + } + //Completes reduce: pops child nodes from the stack and pushes result node into the stack + protected void CompleteReduce(ParsingContext context) { + var resultNode = context.CurrentParserInput; + var childCount = Production.RValues.Count; + //Pop stack + context.ParserStack.Pop(childCount); + //Copy comment block from first child; if comments precede child node, they precede the parent as well. + if (resultNode.ChildNodes.Count > 0) + resultNode.Comments = resultNode.ChildNodes[0].Comments; + //Inherit precedence and associativity, to cover a standard case: BinOp->+|-|*|/; + // BinOp node should inherit precedence from underlying operator symbol. + //TODO: this special case will be handled differently. A ToTerm method should be expanded to allow "combined" terms like "NOT LIKE". + // OLD COMMENT: A special case is SQL operator "NOT LIKE" which consists of 2 tokens. We therefore inherit "max" precedence from any children + if (Production.LValue.Flags.IsSet(TermFlags.InheritPrecedence)) + InheritPrecedence(resultNode); + //Push new node into stack and move to new state + //First read the state from top of the stack + context.CurrentParserState = context.ParserStack.Top.State; + if (context.TracingEnabled) + context.AddTrace(Resources.MsgTracePoppedState, Production.LValue.Name); + #region comments on special case + //Special case: if a non-terminal is Transient (ex: BinOp), then result node is not this NonTerminal, but its its child (ex: symbol). + // Shift action will invoke OnShifting on actual term being shifted (symbol); we need to invoke Shifting even on NonTerminal itself + // - this would be more expected behavior in general. ImpliedPrecHint relies on this + #endregion + if (resultNode.Term != Production.LValue) //special case + Production.LValue.OnShifting(context.SharedParsingEventArgs); + // Shift to new state - execute shift over the non-terminal of the production. + var shift = context.CurrentParserState.Actions[Production.LValue]; + // Execute shift to new state + shift.Execute(context); + //Invoke Reduce event + Production.LValue.OnReduced(context, Production, resultNode); + } + + //This operation helps in situation when Bin expression is declared as BinExpr.Rule = expr + BinOp + expr; + // where BinOp is an OR-combination of operators. + // During parsing, when 'expr, BinOp, expr' is on the top of the stack, + // and incoming symbol is operator, we need to use precedence rule for deciding on the action. + private void InheritPrecedence(ParseTreeNode node) { + for (int i = 0; i < node.ChildNodes.Count; i++) { + var child = node.ChildNodes[i]; + if (child.Precedence == Terminal.NoPrecedence) continue; + node.Precedence = child.Precedence; + node.Associativity = child.Associativity; + return; + } + } + + }//class + + /// Reduces non-terminal marked as Transient by MarkTransient method. + public class ReduceTransientParserAction : ReduceParserAction { + public ReduceTransientParserAction(Production production) : base(production) { } + + protected override ParseTreeNode GetResultNode(ParsingContext context) { + var topIndex = context.ParserStack.Count - 1; + var childCount = Production.RValues.Count; + for (int i = 0; i < childCount; i++) { + var child = context.ParserStack[topIndex - i]; + if (child.IsPunctuationOrEmptyTransient()) continue; + return child; + } + //Otherwise return an empty transient node; if it is part of the list, the list will skip it + var span = context.ComputeStackRangeSpan(childCount); + return new ParseTreeNode(Production.LValue, span); + + } + }//class + + /// Reduces list created by MakePlusRule or MakeListRule methods. + public class ReduceListBuilderParserAction : ReduceParserAction { + + public ReduceListBuilderParserAction(Production production) : base(production) { } + + protected override ParseTreeNode GetResultNode(ParsingContext context) { + int childCount = Production.RValues.Count; + int firstChildIndex = context.ParserStack.Count - childCount; + var listNode = context.ParserStack[firstChildIndex]; //get the list already created - it is the first child node + listNode.Span = context.ComputeStackRangeSpan(childCount); + var listMember = context.ParserStack.Top; //next list member is the last child - at the top of the stack + if (listMember.IsPunctuationOrEmptyTransient()) + return listNode; + listNode.ChildNodes.Add(listMember); + return listNode; + } + }//class + + //List container is an artificial non-terminal created by MakeStarRule method; this actual list is a direct child. + public class ReduceListContainerParserAction : ReduceParserAction { + public ReduceListContainerParserAction(Production production) : base(production) { } + + protected override ParseTreeNode GetResultNode(ParsingContext context) { + int childCount = Production.RValues.Count; + int firstChildIndex = context.ParserStack.Count - childCount; + var span = context.ComputeStackRangeSpan(childCount); + var newNode = new ParseTreeNode(Production.LValue, span); + if (childCount > 0) { //if it is not empty production - might happen for MakeStarRule + var listNode = context.ParserStack[firstChildIndex]; //get the transient list with all members - it is the first child node + newNode.ChildNodes.AddRange(listNode.ChildNodes); //copy all list members + } + return newNode; + + } + }//class + +}//ns diff --git a/Irony/Parsing/Parser/ParserActions/ShiftParserAction.cs b/Irony/Parsing/Parser/ParserActions/ShiftParserAction.cs new file mode 100644 index 0000000..fadc510 --- /dev/null +++ b/Irony/Parsing/Parser/ParserActions/ShiftParserAction.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + public class ShiftParserAction: ParserAction { + public readonly BnfTerm Term; + public readonly ParserState NewState; + + public ShiftParserAction(Construction.LRItem item) : this(item.Core.Current, item.ShiftedItem.State) { } + + public ShiftParserAction(BnfTerm term, ParserState newState) { + Term = term; + NewState = newState; + } + + public override void Execute(ParsingContext context) { + var currInput = context.CurrentParserInput; + currInput.Term.OnShifting(context.SharedParsingEventArgs); + context.ParserStack.Push(currInput, NewState); + context.CurrentParserState = NewState; + context.CurrentParserInput = null; + } + + public override string ToString() { + return string.Format(Resources.LabelActionShift, NewState.Name); + } + + }//class +} diff --git a/Irony/Parsing/Parser/ParserActions/_ParserAction.cs b/Irony/Parsing/Parser/ParserActions/_ParserAction.cs new file mode 100644 index 0000000..6364d2b --- /dev/null +++ b/Irony/Parsing/Parser/ParserActions/_ParserAction.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + public abstract class ParserAction { + + public ParserAction() { } + + public virtual void Execute(ParsingContext context) { + + } + + public override string ToString() { + return Resources.LabelActionUnknown; //should never happen + } + }//class ParserAction + + public class ParserActionTable : Dictionary { } + + +} diff --git a/Irony/Parsing/Parser/ParserDataPrinter.cs b/Irony/Parsing/Parser/ParserDataPrinter.cs new file mode 100644 index 0000000..b6709c0 --- /dev/null +++ b/Irony/Parsing/Parser/ParserDataPrinter.cs @@ -0,0 +1,92 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing; +using Irony.Parsing.Construction; + +namespace Irony.Parsing { + public static class ParserDataPrinter { + + public static string PrintStateList(LanguageData language) { + StringBuilder sb = new StringBuilder(); + foreach (ParserState state in language.ParserData.States) { + sb.Append("State " + state.Name); + if (state.BuilderData.IsInadequate) sb.Append(" (Inadequate)"); + sb.AppendLine(); + var srConflicts = state.BuilderData.GetShiftReduceConflicts(); + if (srConflicts.Count > 0) + sb.AppendLine(" Shift-reduce conflicts on inputs: " + srConflicts.ToString()); + var ssConflicts = state.BuilderData.GetReduceReduceConflicts(); + if (ssConflicts.Count > 0) + sb.AppendLine(" Reduce-reduce conflicts on inputs: " + ssConflicts.ToString()); + //LRItems + if (state.BuilderData.ShiftItems.Count > 0) { + sb.AppendLine(" Shift items:"); + foreach (var item in state.BuilderData.ShiftItems) + sb.AppendLine(" " + item.ToString()); + } + if (state.BuilderData.ReduceItems.Count > 0) { + sb.AppendLine(" Reduce items:"); + foreach(LRItem item in state.BuilderData.ReduceItems) { + var sItem = item.ToString(); + if (item.Lookaheads.Count > 0) + sItem += " [" + item.Lookaheads.ToString() + "]"; + sb.AppendLine(" " + sItem); + } + } + sb.Append(" Transitions: "); + bool atFirst = true; + foreach (BnfTerm key in state.Actions.Keys) { + var action = state.Actions[key] as ShiftParserAction; + if (action == null) + continue; + if (!atFirst) sb.Append(", "); + atFirst = false; + sb.Append(key.ToString()); + sb.Append("->"); + sb.Append(action.NewState.Name); + } + sb.AppendLine(); + sb.AppendLine(); + }//foreach + return sb.ToString(); + } + + public static string PrintTerminals(LanguageData language) { + var termList = language.GrammarData.Terminals.ToList(); + termList.Sort((x, y) => string.Compare(x.Name, y.Name)); + var result = string.Join(Environment.NewLine, termList); + return result; + } + + public static string PrintNonTerminals(LanguageData language) { + StringBuilder sb = new StringBuilder(); + var ntList = language.GrammarData.NonTerminals.ToList(); + ntList.Sort((x, y) => string.Compare(x.Name, y.Name)); + foreach (var nt in ntList) { + sb.Append(nt.Name); + sb.Append(nt.Flags.IsSet(TermFlags.IsNullable) ? " (Nullable) " : string.Empty); + sb.AppendLine(); + foreach (Production pr in nt.Productions) { + sb.Append(" "); + sb.AppendLine(pr.ToString()); + } + }//foreachc nt + return sb.ToString(); + } + + }//class +}//namespace diff --git a/Irony/Parsing/Parser/ParserStack.cs b/Irony/Parsing/Parser/ParserStack.cs new file mode 100644 index 0000000..f096daa --- /dev/null +++ b/Irony/Parsing/Parser/ParserStack.cs @@ -0,0 +1,47 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + public class ParserStack : List { + public ParserStack() : base(200) { } + public void Push(ParseTreeNode nodeInfo) { + base.Add(nodeInfo); + } + public void Push(ParseTreeNode nodeInfo, ParserState state) { + nodeInfo.State = state; + base.Add(nodeInfo); + } + public ParseTreeNode Pop() { + var top = Top; + base.RemoveAt(Count - 1); + return top; + } + public void Pop(int count) { + base.RemoveRange(Count - count, count); + } + public void PopUntil(int finalCount) { + if (finalCount < Count) + Pop(Count - finalCount); + } + public ParseTreeNode Top { + get { + if (Count == 0) return null; + return base[Count - 1]; + } + } + } +} diff --git a/Irony/Parsing/Parser/ParserTrace.cs b/Irony/Parsing/Parser/ParserTrace.cs new file mode 100644 index 0000000..54552d6 --- /dev/null +++ b/Irony/Parsing/Parser/ParserTrace.cs @@ -0,0 +1,51 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + public class ParserTraceEntry { + public ParserState State; + public ParseTreeNode StackTop; + public ParseTreeNode Input; + public string Message; + public bool IsError; + + public ParserTraceEntry(ParserState state, ParseTreeNode stackTop, ParseTreeNode input, string message, bool isError) { + State = state; + StackTop = stackTop; + Input = input; + Message = message; + IsError = isError; + } + }//class + + public class ParserTrace : List { } + + public class ParserTraceEventArgs : EventArgs { + public ParserTraceEventArgs(ParserTraceEntry entry) { + Entry = entry; + } + + public readonly ParserTraceEntry Entry; + + public override string ToString() { + return Entry.ToString(); + } + }//class + + + +}//namespace diff --git a/Irony/Parsing/Parser/ParsingContext.cs b/Irony/Parsing/Parser/ParsingContext.cs new file mode 100644 index 0000000..88af68a --- /dev/null +++ b/Irony/Parsing/Parser/ParsingContext.cs @@ -0,0 +1,291 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Text; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Globalization; + +namespace Irony.Parsing { + + [Flags] + public enum ParseOptions { + Reserved = 0x01, + AnalyzeCode = 0x10, //run code analysis; effective only in Module mode + } + + public enum ParseMode { + File, //default, continuous input file + VsLineScan, // line-by-line scanning in VS integration for syntax highlighting + CommandLine, //line-by-line from console + } + + public enum ParserStatus { + Init, //initial state + Parsing, + Previewing, //previewing tokens + Recovering, //recovering from error + Accepted, + AcceptedPartial, + Error, + } + + // The purpose of this class is to provide a container for information shared + // between parser, scanner and token filters. + public class ParsingContext { + public readonly Parser Parser; + public readonly LanguageData Language; + + //Parser settings + public ParseOptions Options; + public bool TracingEnabled; + public ParseMode Mode = ParseMode.File; + public int MaxErrors = 20; //maximum error count to report + public CultureInfo Culture; //defaults to Grammar.DefaultCulture, might be changed by app code + + #region properties and fields + //Parser fields + public ParseTree CurrentParseTree { get; internal set; } + public readonly TokenStack OpenBraces = new TokenStack(); + public ParserTrace ParserTrace = new ParserTrace(); + internal readonly ParserStack ParserStack = new ParserStack(); + + public ParserState CurrentParserState { get; internal set; } + public ParseTreeNode CurrentParserInput { get; internal set; } + public Token CurrentToken; //The token just scanned by Scanner + public TokenList CurrentCommentTokens = new TokenList(); //accumulated comment tokens + public Token PreviousToken; + public SourceLocation PreviousLineStart; //Location of last line start + + //list for terminals - for current parser state and current input char + public TerminalList CurrentTerminals = new TerminalList(); + + public ISourceStream Source; + + //Internal fields + internal TokenFilterList TokenFilters = new TokenFilterList(); + internal TokenStack BufferedTokens = new TokenStack(); + internal IEnumerator FilteredTokens; //stream of tokens after filter + internal TokenStack PreviewTokens = new TokenStack(); + internal ParsingEventArgs SharedParsingEventArgs; + internal ValidateTokenEventArgs SharedValidateTokenEventArgs; + + public VsScannerStateMap VsLineScanState; //State variable used in line scanning mode for VS integration + + public ParserStatus Status {get; internal set;} + public bool HasErrors; //error flag, once set remains set + + //values dictionary to use by custom language implementations to save some temporary values during parsing + public readonly Dictionary Values = new Dictionary(); + + public int TabWidth = 8; + + #endregion + + + #region constructors + public ParsingContext(Parser parser) { + this.Parser = parser; + Language = Parser.Language; + Culture = Language.Grammar.DefaultCulture; + //This might be a problem for multi-threading - if we have several contexts on parallel threads with different culture. + //Resources.Culture is static property (this is not Irony's fault, this is auto-generated file). + Resources.Culture = Culture; + SharedParsingEventArgs = new ParsingEventArgs(this); + SharedValidateTokenEventArgs = new ValidateTokenEventArgs(this); + } + #endregion + + + #region Events: TokenCreated + public event EventHandler TokenCreated; + + internal void OnTokenCreated() { + if (TokenCreated != null) + TokenCreated(this, SharedParsingEventArgs); + } + #endregion + + #region Error handling and tracing + + public Token CreateErrorToken(string message, params object[] args) { + if (args != null && args.Length > 0) + message = string.Format(message, args); + return Source.CreateToken(Language.Grammar.SyntaxError, message); + } + + public void AddParserError(string message, params object[] args) { + var location = CurrentParserInput == null? Source.Location : CurrentParserInput.Span.Location; + HasErrors = true; + AddParserMessage(ErrorLevel.Error, location, message, args); + } + public void AddParserMessage(ErrorLevel level, SourceLocation location, string message, params object[] args) { + if (CurrentParseTree == null) return; + if (CurrentParseTree.ParserMessages.Count >= MaxErrors) return; + if (args != null && args.Length > 0) + message = string.Format(message, args); + CurrentParseTree.ParserMessages.Add(new LogMessage(level, location, message, CurrentParserState)); + if (TracingEnabled) + AddTrace(true, message); + } + + public void AddTrace(string message, params object[] args) { + AddTrace(false, message, args); + } + public void AddTrace(bool asError, string message, params object[] args) { + if (!TracingEnabled) + return; + if (args != null && args.Length > 0) + message = string.Format(message, args); + ParserTrace.Add(new ParserTraceEntry(CurrentParserState, ParserStack.Top, CurrentParserInput, message, asError)); + } + + #region comments + // Computes set of expected terms in a parser state. While there may be extended list of symbols expected at some point, + // we want to reorganize and reduce it. For example, if the current state expects all arithmetic operators as an input, + // it would be better to not list all operators (+, -, *, /, etc) but simply put "operator" covering them all. + // To achieve this grammar writer can group operators (or any other terminals) into named groups using Grammar's methods + // AddTermReportGroup, AddNoReportGroup etc. Then instead of reporting each operator separately, Irony would include + // a single "group name" to represent them all. + // The "expected report set" is not computed during parser construction (it would take considerable time), + // but does it on demand during parsing, when error is detected and the expected set is actually needed for error message. + // Multi-threading concerns. When used in multi-threaded environment (web server), the LanguageData would be shared in + // application-wide cache to avoid rebuilding the parser data on every request. The LanguageData is immutable, except + // this one case - the expected sets are constructed late by CoreParser on the when-needed basis. + // We don't do any locking here, just compute the set and on return from this function the state field is assigned. + // We assume that this field assignment is an atomic, concurrency-safe operation. The worst thing that might happen + // is "double-effort" when two threads start computing the same set around the same time, and the last one to finish would + // leave its result in the state field. + #endregion + internal static StringSet ComputeGroupedExpectedSetForState(Grammar grammar, ParserState state) { + var terms = new TerminalSet(); + terms.UnionWith(state.ExpectedTerminals); + var result = new StringSet(); + //Eliminate no-report terminals + foreach (var group in grammar.TermReportGroups) + if (group.GroupType == TermReportGroupType.DoNotReport) + terms.ExceptWith(group.Terminals); + //Add normal and operator groups + foreach (var group in grammar.TermReportGroups) + if ((group.GroupType == TermReportGroupType.Normal || group.GroupType == TermReportGroupType.Operator) && + terms.Overlaps(group.Terminals)) { + result.Add(group.Alias); + terms.ExceptWith(group.Terminals); + } + //Add remaining terminals "as is" + foreach (var terminal in terms) + result.Add(terminal.ErrorAlias); + return result; + } + + #endregion + + internal void Reset() { + CurrentParserState = Parser.InitialState; + CurrentParserInput = null; + CurrentCommentTokens = new TokenList(); + ParserStack.Clear(); + HasErrors = false; + ParserStack.Push(new ParseTreeNode(CurrentParserState)); + CurrentParseTree = null; + OpenBraces.Clear(); + ParserTrace.Clear(); + CurrentTerminals.Clear(); + CurrentToken = null; + PreviousToken = null; + PreviousLineStart = new SourceLocation(0, -1, 0); + BufferedTokens.Clear(); + PreviewTokens.Clear(); + Values.Clear(); + foreach (var filter in TokenFilters) + filter.Reset(); + } + + public void SetSourceLocation(SourceLocation location) { + foreach (var filter in TokenFilters) + filter.OnSetSourceLocation(location); + Source.Location = location; + } + + public SourceSpan ComputeStackRangeSpan(int nodeCount) { + if (nodeCount == 0) + return new SourceSpan(CurrentParserInput.Span.Location, 0); + var first = ParserStack[ParserStack.Count - nodeCount]; + var last = ParserStack.Top; + return new SourceSpan(first.Span.Location, last.Span.EndPosition - first.Span.Location.Position); + } + + + #region Expected term set computations + public StringSet GetExpectedTermSet() { + if (CurrentParserState == null) + return new StringSet(); + //See note about multi-threading issues in ComputeReportedExpectedSet comments. + if (CurrentParserState.ReportedExpectedSet == null) + CurrentParserState.ReportedExpectedSet = Construction.ParserDataBuilder.ComputeGroupedExpectedSetForState(Language.Grammar, CurrentParserState); + //Filter out closing braces which are not expected based on previous input. + // While the closing parenthesis ")" might be expected term in a state in general, + // if there was no opening parenthesis in preceding input then we would not + // expect a closing one. + var expectedSet = FilterBracesInExpectedSet(CurrentParserState.ReportedExpectedSet); + return expectedSet; + } + + private StringSet FilterBracesInExpectedSet(StringSet stateExpectedSet) { + var result = new StringSet(); + result.UnionWith(stateExpectedSet); + //Find what brace we expect + var nextClosingBrace = string.Empty; + if (OpenBraces.Count > 0) { + var lastOpenBraceTerm = OpenBraces.Peek().KeyTerm; + var nextClosingBraceTerm = lastOpenBraceTerm.IsPairFor as KeyTerm; + if (nextClosingBraceTerm != null) + nextClosingBrace = nextClosingBraceTerm.Text; + } + //Now check all closing braces in result set, and leave only nextClosingBrace + foreach (var term in Language.Grammar.KeyTerms.Values) { + if (term.Flags.IsSet(TermFlags.IsCloseBrace)) { + var brace = term.Text; + if (result.Contains(brace) && brace != nextClosingBrace) + result.Remove(brace); + } + }//foreach term + return result; + } + + #endregion + + + }//class + + // A struct used for packing/unpacking ScannerState int value; used for VS integration. + // When Terminal produces incomplete token, it sets + // this state to non-zero value; this value identifies this terminal as the one who will continue scanning when + // it resumes, and the terminal's internal state when there may be several types of multi-line tokens for one terminal. + // For ex., there maybe several types of string literal like in Python. + [StructLayout(LayoutKind.Explicit)] + public struct VsScannerStateMap { + [FieldOffset(0)] + public int Value; + [FieldOffset(0)] + public byte TerminalIndex; //1-based index of active multiline term in MultilineTerminals + [FieldOffset(1)] + public byte TokenSubType; //terminal subtype (used in StringLiteral to identify string kind) + [FieldOffset(2)] + public short TerminalFlags; //Terminal flags + }//struct + + +} diff --git a/Irony/Parsing/Parser/ParsingEventArgs.cs b/Irony/Parsing/Parser/ParsingEventArgs.cs new file mode 100644 index 0000000..78b7d8c --- /dev/null +++ b/Irony/Parsing/Parser/ParsingEventArgs.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + public class ParsingEventArgs : EventArgs { + public readonly ParsingContext Context; + public ParsingEventArgs(ParsingContext context) { + Context = context; + } + } + + public class ReducedEventArgs : ParsingEventArgs { + public readonly Production ReducedProduction; + public readonly ParseTreeNode ResultNode; + public ReducedEventArgs(ParsingContext context, Production reducedProduction, ParseTreeNode resultNode) : base(context) { + ReducedProduction = reducedProduction; + ResultNode = resultNode; + } + } + + public class ValidateTokenEventArgs : ParsingEventArgs { + public ValidateTokenEventArgs(ParsingContext context) : base(context) { } + + public Token Token { + get { return Context.CurrentToken; } + }//Token + + public void ReplaceToken(Token token) { + Context.CurrentToken = token; + } + public void SetError(string errorMessage, params object[] messageArgs) { + Context.CurrentToken = Context.CreateErrorToken(errorMessage, messageArgs); + } + //Rejects the token; use it when there's more than one terminal that can be used to scan the input and ValidateToken is used + // to help Scanner make the decision. Once the token is rejected, the scanner will move to the next Terminal (with lower priority) + // and will try to produce token. + public void RejectToken() { + Context.CurrentToken = null; + } + }//class + +}//namespace diff --git a/Irony/Parsing/Parser/SpecialActionsHints/ConditionalParserAction.cs b/Irony/Parsing/Parser/SpecialActionsHints/ConditionalParserAction.cs new file mode 100644 index 0000000..3ff1b4e --- /dev/null +++ b/Irony/Parsing/Parser/SpecialActionsHints/ConditionalParserAction.cs @@ -0,0 +1,74 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + public enum PreferredActionType { + Shift, + Reduce, + } + + public class ConditionalParserAction : ParserAction { + + #region embedded types + public delegate bool ConditionChecker(ParsingContext context); + + public class ConditionalEntry { + public ConditionChecker Condition; + public ParserAction Action; + public string Description; //for tracing + public ConditionalEntry(ConditionChecker condition, ParserAction action, string description) { + Condition = condition; + Action = action; + Description = description; + } + public override string ToString() { + return Description + "; action: " + Action.ToString(); + } + } + + public class ConditionalEntryList : List { } + #endregion + + public ConditionalEntryList ConditionalEntries = new ConditionalEntryList(); + public ParserAction DefaultAction; + + public override void Execute(ParsingContext context) { + var traceEnabled = context.TracingEnabled; + if (traceEnabled) context.AddTrace("Conditional Parser Action."); + for (int i = 0; i < ConditionalEntries.Count; i++) { + var ce = ConditionalEntries[i]; + if (traceEnabled) context.AddTrace(" Checking condition: " + ce.Description); + if (ce.Condition(context)) { + if (traceEnabled) context.AddTrace(" Condition is TRUE, executing action: " + ce.Action.ToString()); + ce.Action.Execute(context); + return; + } + } + //if no conditions matched, execute default action + if (DefaultAction == null) { + context.AddParserError("Fatal parser error: no conditions matched in conditional parser action, and default action is null." + + " State: {0}", context.CurrentParserState.Name); + context.Parser.RecoverFromError(); + return; + } + if (traceEnabled) context.AddTrace(" All conditions failed, executing default action: " + DefaultAction.ToString()); + DefaultAction.Execute(context); + }//method + + }//class +} diff --git a/Irony/Parsing/Parser/SpecialActionsHints/CustomActionHintAction.cs b/Irony/Parsing/Parser/SpecialActionsHints/CustomActionHintAction.cs new file mode 100644 index 0000000..15003b8 --- /dev/null +++ b/Irony/Parsing/Parser/SpecialActionsHints/CustomActionHintAction.cs @@ -0,0 +1,98 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + //These two delegates define custom methods that Grammar can implement to execute custom action + public delegate void PreviewActionMethod(CustomParserAction action); + public delegate void ExecuteActionMethod(ParsingContext context, CustomParserAction action); + + public class CustomActionHint: GrammarHint { + private ExecuteActionMethod _executeMethod; + private PreviewActionMethod _previewMethod; + + public CustomActionHint(ExecuteActionMethod executeMethod, PreviewActionMethod previewMethod = null) { + _executeMethod = executeMethod; + _previewMethod = previewMethod; + } + + public override void Apply(LanguageData language, Construction.LRItem owner) { + //Create custom action and put it into state.Actions table + var state = owner.State; + var action = new CustomParserAction(language, state, _executeMethod); + if (_previewMethod != null) + _previewMethod(action); + if (!state.BuilderData.IsInadequate) // adequate state, with a single possible action which is DefaultAction + state.DefaultAction = action; + else if (owner.Core.Current != null) //shift action + state.Actions[owner.Core.Current] = action; + else foreach (var lkh in owner.Lookaheads) + state.Actions[lkh] = action; + //We consider all conflicts handled by the action + state.BuilderData.Conflicts.Clear(); + }//method + + }//Hint class + + + // CustomParserAction is in fact action selector: it allows custom Grammar code to select the action to execute from a set of + // shift/reduce actions available in this state. + public class CustomParserAction : ParserAction { + public LanguageData Language; + public ParserState State; + public ExecuteActionMethod ExecuteRef; + public TerminalSet Conflicts = new TerminalSet(); + public IList ShiftActions = new List(); + public IList ReduceActions = new List(); + public object CustomData; + + public CustomParserAction(LanguageData language, ParserState state, + ExecuteActionMethod executeRef) { + Language = language; + State = state; + ExecuteRef = executeRef; + Conflicts.UnionWith(state.BuilderData.Conflicts); + // Create default shift and reduce actions + foreach (var shiftItem in state.BuilderData.ShiftItems) + ShiftActions.Add(new ShiftParserAction(shiftItem)); + foreach (var item in state.BuilderData.ReduceItems) + ReduceActions.Add(ReduceParserAction.Create(item.Core.Production)); + } + + public override void Execute(ParsingContext context) { + if (context.TracingEnabled) + context.AddTrace(Resources.MsgTraceExecCustomAction); + //States with DefaultAction do NOT read input, so we read it here + if (context.CurrentParserInput == null) + context.Parser.ReadInput(); + // Remember old state and input; if they don't change after custom action - it is error, we may fall into an endless loop + var oldState = context.CurrentParserState; + var oldInput = context.CurrentParserInput; + ExecuteRef(context, this); + //Prevent from falling into an infinite loop + if (context.CurrentParserState == oldState && context.CurrentParserInput == oldInput) { + context.AddParserError(Resources.MsgErrorCustomActionDidNotAdvance); + context.Parser.RecoverFromError(); + } + }//method + + public override string ToString() { + return "CustomParserAction"; + } + }//class + +}//ns diff --git a/Irony/Parsing/Parser/SpecialActionsHints/ImpliedPrecedenceHint.cs b/Irony/Parsing/Parser/SpecialActionsHints/ImpliedPrecedenceHint.cs new file mode 100644 index 0000000..311b87a --- /dev/null +++ b/Irony/Parsing/Parser/SpecialActionsHints/ImpliedPrecedenceHint.cs @@ -0,0 +1,54 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + //Note: This in incomplete implementation. + // this implementation sets precedence only on operator symbols that are already "shifted" into the parser stack, + // ie those on the "left" of precedence comparison. It does not set precedence when operator symbol first appears in parser + // input. This works OK for unary operator but might break some advanced scenarios. + + public class ImpliedPrecedenceHint : GrammarHint { + public const int ImpliedPrecedenceCustomFlag = 0x01000000; // a flag to mark a state for setting implied precedence + + //GrammarHint inherits Precedence and Associativity members from BnfTerm; we'll use them to store implied values for this hint + + public ImpliedPrecedenceHint(int precedence, Associativity associativity) { + Precedence = precedence; + Associativity = associativity; + } + + public override void Apply(LanguageData language, Construction.LRItem owner) { + //Check that owner is not final - we can imply precedence only in shift context + var curr = owner.Core.Current; + if (curr == null) + return; + //mark the state, to make sure we do stuff in Term_Shifting event handler only in appropriate states + owner.State.CustomFlags |= ImpliedPrecedenceCustomFlag; + curr.Shifting += Term_Shifting; + } + + void Term_Shifting(object sender, ParsingEventArgs e) { + //Set the values only if we are in the marked state + if (!e.Context.CurrentParserState.CustomFlagIsSet(ImpliedPrecedenceCustomFlag)) + return; + e.Context.CurrentParserInput.Associativity = Associativity; + e.Context.CurrentParserInput.Precedence = Precedence; + } + + }//class +} diff --git a/Irony/Parsing/Parser/SpecialActionsHints/PrecedenceBasedParserAction.cs b/Irony/Parsing/Parser/SpecialActionsHints/PrecedenceBasedParserAction.cs new file mode 100644 index 0000000..f325889 --- /dev/null +++ b/Irony/Parsing/Parser/SpecialActionsHints/PrecedenceBasedParserAction.cs @@ -0,0 +1,54 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + public class PrecedenceBasedParserAction : ConditionalParserAction { + ShiftParserAction _shiftAction; + ReduceParserAction _reduceAction; + + public PrecedenceBasedParserAction(BnfTerm shiftTerm, ParserState newShiftState, Production reduceProduction) { + _reduceAction = new ReduceParserAction(reduceProduction); + var reduceEntry = new ConditionalEntry(CheckMustReduce, _reduceAction, "(Precedence comparison)"); + base.ConditionalEntries.Add(reduceEntry); + base.DefaultAction = _shiftAction = new ShiftParserAction(shiftTerm, newShiftState); + } + + private static bool CheckMustReduce(ParsingContext context) { + var input = context.CurrentParserInput; + for (int i = context.ParserStack.Count - 1; i >= 0; i--) { + var prevNode = context.ParserStack[i]; + if (prevNode == null) continue; + if (prevNode.Precedence == BnfTerm.NoPrecedence) continue; + //if previous operator has the same precedence then use associativity + if (prevNode.Precedence == input.Precedence) + return (input.Associativity == Associativity.Left); //if true then Reduce + else + return (prevNode.Precedence > input.Precedence); //if true then Reduce + } + //If no operators found on the stack, do shift + return false; + } + + public override string ToString() { + return string.Format(Resources.LabelActionOp, _shiftAction.NewState.Name, _reduceAction.Production.ToStringQuoted()); + } + + }//class + + +}//namespace diff --git a/Irony/Parsing/Parser/SpecialActionsHints/PrecedenceHint.cs b/Irony/Parsing/Parser/SpecialActionsHints/PrecedenceHint.cs new file mode 100644 index 0000000..43b69da --- /dev/null +++ b/Irony/Parsing/Parser/SpecialActionsHints/PrecedenceHint.cs @@ -0,0 +1,53 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Irony.Parsing.Construction; + +namespace Irony.Parsing { + + /// A hint to use precedence. + /// + /// Not used directly in grammars; injected automatically by system in states having conflicts on operator symbols. + /// The purpose of the hint is make handling precedence similar to other conflict resolution methods - through hints + /// activated during parser construction. The hint code analyzes the conflict and resolves it by adding custom or general action + /// for a conflicting input. + /// + public class PrecedenceHint : GrammarHint { + public override void Apply(LanguageData language, LRItem owner) { + var state = owner.State; + var allConflicts = state.BuilderData.Conflicts; + if (allConflicts.Count == 0) + return; + //Find all conflicts that can be resolved by operator precedence + // SL does not support Find extension, so we do it with explicit loop + var operConflicts = new List(); + foreach(var c in allConflicts) + if (c.Flags.IsSet(TermFlags.IsOperator)) + operConflicts.Add(c); + foreach (var conflict in operConflicts) { + var newState = state.BuilderData.GetNextState(conflict); + var reduceItem = state.BuilderData.ReduceItems.SelectByLookahead(conflict).First(); //should be only one + state.Actions[conflict] = new PrecedenceBasedParserAction(conflict, newState, reduceItem.Core.Production); + allConflicts.Remove(conflict); + }//foreach conflict + } + + }//class + + + +}//namespace diff --git a/Irony/Parsing/Parser/SpecialActionsHints/PreferredActionHint.cs b/Irony/Parsing/Parser/SpecialActionsHints/PreferredActionHint.cs new file mode 100644 index 0000000..a2b79ff --- /dev/null +++ b/Irony/Parsing/Parser/SpecialActionsHints/PreferredActionHint.cs @@ -0,0 +1,57 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Irony.Parsing.Construction; + +namespace Irony.Parsing { + + public class PreferredActionHint : GrammarHint { + PreferredActionType ActionType; + public PreferredActionHint(PreferredActionType actionType) { + ActionType = actionType; + } + public override void Apply(LanguageData language, LRItem owner) { + var state = owner.State; + var conflicts = state.BuilderData.Conflicts; + if (conflicts.Count == 0) return; + switch (ActionType) { + case PreferredActionType.Shift: + var currTerm = owner.Core.Current as Terminal; + if (currTerm == null || !conflicts.Contains(currTerm)) return; //nothing to do + //Current term for shift item (hint owner) is a conflict - resolve it with shift action + var newState = owner.ShiftedItem.State; + var shiftAction = new ShiftParserAction(owner); + state.Actions[currTerm] = shiftAction; + conflicts.Remove(currTerm); + return; + case PreferredActionType.Reduce: + if (!owner.Core.IsFinal) return; //we take care of reduce items only here + //we have a reduce item with "Reduce" hint. Check if any of lookaheads are in conflict + ReduceParserAction reduceAction = null; + foreach (var lkhead in owner.Lookaheads) + if (conflicts.Contains(lkhead)) { + if (reduceAction == null) + reduceAction = new ReduceParserAction(owner.Core.Production); + state.Actions[lkhead] = reduceAction; + conflicts.Remove(lkhead); + } + return; + }//switch + }//method + }//class + + +} diff --git a/Irony/Parsing/Parser/SpecialActionsHints/TokenPreviewHint.cs b/Irony/Parsing/Parser/SpecialActionsHints/TokenPreviewHint.cs new file mode 100644 index 0000000..61281e4 --- /dev/null +++ b/Irony/Parsing/Parser/SpecialActionsHints/TokenPreviewHint.cs @@ -0,0 +1,163 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +// Original implementation is contributed by Alexey Yakovlev (yallie) + +namespace Irony.Parsing { + using ConditionalEntry = ConditionalParserAction.ConditionalEntry; + + public class TokenPreviewHint : GrammarHint { + public int MaxPreviewTokens = 1000; + private PreferredActionType _actionType; + private string _firstString; + private StringSet _beforeStrings = new StringSet(); + private Terminal _firstTerminal; + private TerminalSet _beforeTerminals = new TerminalSet(); + private string _description; + + public TokenPreviewHint(PreferredActionType actionType, string thisSymbol, params string[] comesBefore) { + _actionType = actionType; + _firstString = thisSymbol; + _beforeStrings.AddRange(comesBefore); + } + public TokenPreviewHint(PreferredActionType actionType, Terminal thisTerm, params Terminal[] comesBefore) { + _actionType = actionType; + _firstTerminal = thisTerm; + _beforeTerminals.UnionWith(comesBefore); + } + + + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + // convert strings to terminals, if needed + _firstTerminal = _firstTerminal ?? Grammar.ToTerm(_firstString); + if (_beforeStrings.Count > 0) { + //SL pukes here, it does not support co/contravariance in full, we have to do it long way + foreach (var s in _beforeStrings) + _beforeTerminals.Add(Grammar.ToTerm(s)); + } + //Build description + var beforeTerms = string.Join(" ", _beforeTerminals.Select(t => t.Name)); + _description = string.Format("{0} if {1} comes before {2}.", _actionType, _firstTerminal.Name, beforeTerms); + } + + public override string ToString() { + if (_description == null) + _description = _actionType.ToString() + " if ..."; + return _description; + } + + public override void Apply(LanguageData language, Construction.LRItem owner) { + var state = owner.State; + if (!state.BuilderData.IsInadequate) return; //the state is adequate, we don't need to do anything + var conflicts = state.BuilderData.Conflicts; + // Note that remove lookaheads from the state conflicts set at the end of this method - to let parser builder know + // that this conflict is taken care of. + // On the other head we may call this method multiple times for different LRItems if we have multiple hints in the same state. + // Since we remove lookahead from conflicts on the first call, on the consequitive calls it will not be a conflict - + // but we still need to add a new conditional entry to a conditional parser action for this lookahead. + // Thus we process the lookahead anyway, even if it is not a conflict. + // if (conflicts.Count == 0) return; -- this is a wrong thing to do + switch (_actionType) { + case PreferredActionType.Reduce: + if (!owner.Core.IsFinal) return; + //it is reduce action; find lookaheads in conflict + var lkhs = owner.Lookaheads; + if (lkhs.Count == 0) return; //if no conflicts then nothing to do + var reduceAction = new ReduceParserAction(owner.Core.Production); + var reduceCondEntry = new ConditionalEntry(CheckCondition, reduceAction, _description); + foreach (var lkh in lkhs) { + AddConditionalEntry(state, lkh, reduceCondEntry); + if (conflicts.Contains(lkh)) + conflicts.Remove(lkh); + } + break; + case PreferredActionType.Shift: + var curr = owner.Core.Current as Terminal; + if (curr == null) return; //it is either reduce item, or curr is a NonTerminal - we cannot shift it + var shiftAction = new ShiftParserAction(owner); + var shiftCondEntry = new ConditionalEntry(CheckCondition, shiftAction, _description); + AddConditionalEntry(state, curr, shiftCondEntry); + if (conflicts.Contains(curr)) + conflicts.Remove(curr); + break; + } + + + }//method + + private bool CheckCondition(ParsingContext context) { + var scanner = context.Parser.Scanner; + try { + var eof = Grammar.Eof; + var count = 0; + scanner.BeginPreview(); + var token = scanner.GetToken(); + while (token != null && token.Terminal != eof) { + if (token.Terminal == _firstTerminal) + return true; //found! + if (_beforeTerminals.Contains(token.Terminal)) + return false; + if (++count > MaxPreviewTokens && MaxPreviewTokens > 0) + return false; + token = scanner.GetToken(); + } + return false; + } finally { + scanner.EndPreview(true); + } + } + + //Check if there is an action already in state for this term; if yes, and it is Conditional action, + // then simply add an extra conditional entry to it. If an action does not exist, or it is not conditional, + // create new conditional action for this term. + private void AddConditionalEntry(ParserState state, BnfTerm term, ConditionalEntry entry) { + ParserAction oldAction; + ConditionalParserAction condAction = null; + if (state.Actions.TryGetValue(term, out oldAction)) + condAction = oldAction as ConditionalParserAction; + if (condAction == null) { //there's no old action, or it is not conditional; create new conditional action + condAction = new ConditionalParserAction(); + condAction.DefaultAction = oldAction; + state.Actions[term] = condAction; + } + condAction.ConditionalEntries.Add(entry); + if (condAction.DefaultAction == null) + condAction.DefaultAction = FindDefaultAction(state, term); + if (condAction.DefaultAction == null) //if still no action, then use the cond. action as default. + condAction.DefaultAction = entry.Action; + } + + //Find an LR item without hints compatible with term (either shift on term or reduce with term as lookahead); + // this item without hints would become our default. We assume that other items have hints, and when conditions + // on all these hints fail, we chose this remaining item without hints. + private ParserAction FindDefaultAction(ParserState state, BnfTerm term) { + //First check reduce items + var reduceItems = state.BuilderData.ReduceItems.SelectByLookahead(term as Terminal); + foreach (var item in reduceItems) + if (item.Core.Hints.Count == 0) + return ReduceParserAction.Create(item.Core.Production); + var shiftItem = state.BuilderData.ShiftItems.SelectByCurrent(term).FirstOrDefault(); + if (shiftItem != null) + return new ShiftParserAction(shiftItem); + //if everything failed, returned first reduce item + return null; + } + + }//class + +} diff --git a/Irony/Parsing/Parser/SyntaxError.cs b/Irony/Parsing/Parser/SyntaxError.cs new file mode 100644 index 0000000..0cc52ba --- /dev/null +++ b/Irony/Parsing/Parser/SyntaxError.cs @@ -0,0 +1,42 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + //Container for syntax error + public class SyntaxError { + public SyntaxError(SourceLocation location, string message, ParserState parserState) { + Location = location; + Message = message; + ParserState = parserState; + } + + public readonly SourceLocation Location; + public readonly string Message; + public ParserState ParserState; + + public override string ToString() { + return Message; + } + }//class + + public class SyntaxErrorList : List { + public static int ByLocation(SyntaxError x, SyntaxError y) { + return SourceLocation.Compare(x.Location, y.Location); + } + } + +}//namespace diff --git a/Irony/Parsing/Scanner/Scanner.cs b/Irony/Parsing/Scanner/Scanner.cs new file mode 100644 index 0000000..bc91f25 --- /dev/null +++ b/Irony/Parsing/Scanner/Scanner.cs @@ -0,0 +1,322 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; + +namespace Irony.Parsing { + + //Scanner class. The Scanner's function is to transform a stream of characters into aggregates/words or lexemes, + // like identifier, number, literal, etc. + + public class Scanner { + #region Properties and Fields: Data, _source + public readonly ScannerData Data; + public readonly Parser Parser; + Grammar _grammar; + //buffered tokens can come from expanding a multi-token, when Terminal.TryMatch() returns several tokens packed into one token + + private ParsingContext Context { + get { return Parser.Context; } + } + #endregion + + public Scanner(Parser parser) { + Parser = parser; + Data = parser.Language.ScannerData; + _grammar = parser.Language.Grammar; + //create token streams + var tokenStream = GetUnfilteredTokens(); + //chain all token filters + Context.TokenFilters.Clear(); + _grammar.CreateTokenFilters(Data.Language, Context.TokenFilters); + foreach (TokenFilter filter in Context.TokenFilters) { + tokenStream = filter.BeginFiltering(Context, tokenStream); + } + Context.FilteredTokens = tokenStream.GetEnumerator(); + } + + internal void Reset() { + } + + public Token GetToken() { + //get new token from pipeline + if (!Context.FilteredTokens.MoveNext()) return null; + var token = Context.FilteredTokens.Current; + if (Context.Status == ParserStatus.Previewing) + Context.PreviewTokens.Push(token); + else + Context.CurrentParseTree.Tokens.Add(token); + return token; + } + + //This is iterator method, so it returns immediately when called directly + // returns unfiltered, "raw" token stream + private IEnumerable GetUnfilteredTokens() { + //We don't do "while(!_source.EOF())... because on EOF() we need to continue and produce EOF token + while (true) { + Context.PreviousToken = Context.CurrentToken; + Context.CurrentToken = null; + NextToken(); + Context.OnTokenCreated(); + yield return Context.CurrentToken; + //Don't yield break, continue returning EOF + }//while + }// method + + #region Scanning tokens + private void NextToken() { + //1. Check if there are buffered tokens + if(Context.BufferedTokens.Count > 0) { + Context.CurrentToken = Context.BufferedTokens.Pop(); + return; + } + //2. Skip whitespace. + _grammar.SkipWhitespace(Context.Source); + //3. That's the token start, calc location (line and column) + Context.Source.Position = Context.Source.PreviewPosition; + //4. Check for EOF + if (Context.Source.EOF()) { + Context.CurrentToken = new Token(_grammar.Eof, Context.Source.Location, string.Empty, _grammar.Eof.Name);; + return; + } + //5. Actually scan the source text and construct a new token + ScanToken(); + }//method + + //Scans the source text and constructs a new token + private void ScanToken() { + if (!MatchNonGrammarTerminals() && !MatchRegularTerminals()) { + //we are in error already; try to match ANY terminal and let the parser report an error + MatchAllTerminals(); //try to match any terminal out there + } + var token = Context.CurrentToken; + //If we have normal token then return it + if (token != null && !token.IsError()) { + var src = Context.Source; + //set position to point after the result token + src.PreviewPosition = src.Position + token.Length; + src.Position = src.PreviewPosition; + return; + } + //we have an error: either error token or no token at all + if (token == null) //if no token then create error token + Context.CurrentToken = Context.CreateErrorToken(Resources.ErrInvalidChar, Context.Source.PreviewChar); + Recover(); + } + + private bool MatchNonGrammarTerminals() { + TerminalList terms; + if(!Data.NonGrammarTerminalsLookup.TryGetValue(Context.Source.PreviewChar, out terms)) + return false; + foreach(var term in terms) { + Context.Source.PreviewPosition = Context.Source.Location.Position; + Context.CurrentToken = term.TryMatch(Context, Context.Source); + if (Context.CurrentToken != null) + term.OnValidateToken(Context); + if (Context.CurrentToken != null) { + //check if we need to fire LineStart token before this token; + // we do it only if the token is not a comment; comments should be ignored by the outline logic + var token = Context.CurrentToken; + if (token.Category == TokenCategory.Content && NeedLineStartToken(token.Location)) { + Context.BufferedTokens.Push(token); //buffer current token; we'll eject LineStart instead + Context.Source.Location = token.Location; //set it back to the start of the token + Context.CurrentToken = Context.Source.CreateToken(_grammar.LineStartTerminal); //generate LineStart + Context.PreviousLineStart = Context.Source.Location; //update LineStart + } + return true; + }//if + }//foreach term + Context.Source.PreviewPosition = Context.Source.Location.Position; + return false; + } + + private bool NeedLineStartToken(SourceLocation forLocation) { + return _grammar.LanguageFlags.IsSet(LanguageFlags.EmitLineStartToken) && + forLocation.Line > Context.PreviousLineStart.Line; + } + + private bool MatchRegularTerminals() { + //We need to eject LineStart BEFORE we try to produce a real token; this LineStart token should reach + // the parser, make it change the state and with it to change the set of expected tokens. So when we + // finally move to scan the real token, the expected terminal set is correct. + if (NeedLineStartToken(Context.Source.Location)) { + Context.CurrentToken = Context.Source.CreateToken(_grammar.LineStartTerminal); + Context.PreviousLineStart = Context.Source.Location; + return true; + } + //Find matching terminal + // First, try terminals with explicit "first-char" prefixes, selected by current char in source + ComputeCurrentTerminals(); + //If we have more than one candidate; let grammar method select + if (Context.CurrentTerminals.Count > 1) + _grammar.OnScannerSelectTerminal(Context); + + MatchTerminals(); + //If we don't have a token from terminals, try Grammar's method + if (Context.CurrentToken == null) + Context.CurrentToken = _grammar.TryMatch(Context, Context.Source); + if (Context.CurrentToken is MultiToken) + UnpackMultiToken(); + return Context.CurrentToken != null; + }//method + + // This method is a last attempt by scanner to match ANY terminal, after regular matching (by input char) had failed. + // Likely this will produce some token which is invalid for current parser state (for ex, identifier where a number + // is expected); in this case the parser will report an error as "Error: expected number". + // if this matching fails, the scanner will produce an error as "unexpected character." + private bool MatchAllTerminals() { + Context.CurrentTerminals.Clear(); + Context.CurrentTerminals.AddRange(Data.Language.GrammarData.Terminals); + MatchTerminals(); + if (Context.CurrentToken is MultiToken) + UnpackMultiToken(); + return Context.CurrentToken != null; + } + + //If token is MultiToken then push all its child tokens into _bufferdTokens and return the first token in buffer + private void UnpackMultiToken() { + var mtoken = Context.CurrentToken as MultiToken; + if (mtoken == null) return; + for (int i = mtoken.ChildTokens.Count-1; i >= 0; i--) + Context.BufferedTokens.Push(mtoken.ChildTokens[i]); + Context.CurrentToken = Context.BufferedTokens.Pop(); + } + + private void ComputeCurrentTerminals() { + Context.CurrentTerminals.Clear(); + TerminalList termsForCurrentChar; + if(!Data.TerminalsLookup.TryGetValue(Context.Source.PreviewChar, out termsForCurrentChar)) + termsForCurrentChar = Data.NoPrefixTerminals; + //if we are recovering, previewing or there's no parser state, then return list as is + if(Context.Status == ParserStatus.Recovering || Context.Status == ParserStatus.Previewing + || Context.CurrentParserState == null || _grammar.LanguageFlags.IsSet(LanguageFlags.DisableScannerParserLink) + || Context.Mode == ParseMode.VsLineScan) { + Context.CurrentTerminals.AddRange(termsForCurrentChar); + return; + } + // Try filtering terms by checking with parser which terms it expects; + var parserState = Context.CurrentParserState; + foreach(var term in termsForCurrentChar) { + //Note that we check the OutputTerminal with parser, not the term itself; + //in most cases it is the same as term, but not always + if (parserState.ExpectedTerminals.Contains(term.OutputTerminal) || _grammar.NonGrammarTerminals.Contains(term)) + Context.CurrentTerminals.Add(term); + } + + }//method + + private void MatchTerminals() { + Token priorToken = null; + for (int i=0; i term.Priority) + return; + //Reset source position and try to match + Context.Source.PreviewPosition = Context.Source.Location.Position; + var token = term.TryMatch(Context, Context.Source); + if (token == null) continue; + //skip it if it is shorter than previous token + if (priorToken != null && !priorToken.IsError() && (token.Length < priorToken.Length)) + continue; + Context.CurrentToken = token; //now it becomes current token + term.OnValidateToken(Context); //validate it + if (Context.CurrentToken != null) + priorToken = Context.CurrentToken; + } + }//method + + #endregion + + #region VS Integration methods + //Use this method for VS integration; VS language package requires scanner that returns tokens one-by-one. + // Start and End positions required by this scanner may be derived from Token : + // start=token.Location.Position; end=start + token.Length; + public Token VsReadToken(ref int state) { + Context.VsLineScanState.Value = state; + if (Context.Source.EOF()) return null; + if (state == 0) + NextToken(); + else { + Terminal term = Data.MultilineTerminals[Context.VsLineScanState.TerminalIndex - 1]; + Context.CurrentToken = term.TryMatch(Context, Context.Source); + } + //set state value from context + state = Context.VsLineScanState.Value; + if (Context.CurrentToken != null && Context.CurrentToken.Terminal == _grammar.Eof) + return null; + return Context.CurrentToken; + } + public void VsSetSource(string text, int offset) { + var line = Context.Source==null ? 0 : Context.Source.Location.Line; + var newLoc = new SourceLocation(offset, line + 1, 0); + Context.Source = new SourceStream(text, Context.Language.Grammar.CaseSensitive, Context.TabWidth, newLoc); + } + #endregion + + #region Error recovery + //Simply skip until whitespace or delimiter character + private bool Recover() { + var src = Context.Source; + src.PreviewPosition++; + while (!Context.Source.EOF()) { + if(_grammar.IsWhitespaceOrDelimiter(src.PreviewChar)) { + src.Position = src.PreviewPosition; + return true; + } + src.PreviewPosition++; + } + return false; + } + #endregion + + #region TokenPreview + //Preview mode allows custom code in grammar to help parser decide on appropriate action in case of conflict + // Preview process is simply searching for particular tokens in "preview set", and finding out which of the + // tokens will come first. + // In preview mode, tokens returned by FetchToken are collected in _previewTokens list; after finishing preview + // the scanner "rolls back" to original position - either by directly restoring the position, or moving the preview + // tokens into _bufferedTokens list, so that they will read again by parser in normal mode. + // See c# grammar sample for an example of using preview methods + SourceLocation _previewStartLocation; + + //Switches Scanner into preview mode + public void BeginPreview() { + Context.Status = ParserStatus.Previewing; + _previewStartLocation = Context.Source.Location; + Context.PreviewTokens.Clear(); + } + + //Ends preview mode + public void EndPreview(bool keepPreviewTokens) { + if (keepPreviewTokens) { + //insert previewed tokens into buffered list, so we don't recreate them again + while (Context.PreviewTokens.Count > 0) + Context.BufferedTokens.Push(Context.PreviewTokens.Pop()); + } else + Context.SetSourceLocation(_previewStartLocation); + Context.PreviewTokens.Clear(); + Context.Status = ParserStatus.Parsing; + } + #endregion + + + + + }//class + +}//namespace diff --git a/Irony/Parsing/Scanner/SourceLocation.cs b/Irony/Parsing/Scanner/SourceLocation.cs new file mode 100644 index 0000000..0ae2496 --- /dev/null +++ b/Irony/Parsing/Scanner/SourceLocation.cs @@ -0,0 +1,73 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + public struct SourceLocation { + public int Position; + /// Source line number, 0-based. + public int Line; + /// Source column number, 0-based. + public int Column; + public SourceLocation(int position, int line, int column) { + Position = position; + Line = line; + Column = column; + } + //Line/col are zero-based internally + public override string ToString() { + return string.Format(Resources.FmtRowCol, Line + 1, Column + 1); + } + //Line and Column displayed to user should be 1-based + public string ToUiString() { + return string.Format(Resources.FmtRowCol, Line + 1, Column + 1); + } + public static int Compare(SourceLocation x, SourceLocation y) { + if (x.Position < y.Position) return -1; + if (x.Position == y.Position) return 0; + return 1; + } + public static SourceLocation Empty { + get { return _empty; } + } static SourceLocation _empty = new SourceLocation(); + + public static SourceLocation operator + (SourceLocation x, SourceLocation y) { + return new SourceLocation(x.Position + y.Position, x.Line + y.Line, x.Column + y.Column); + } + public static SourceLocation operator + (SourceLocation x, int offset) { + return new SourceLocation(x.Position + offset, x.Line, x.Column + offset); + } + }//SourceLocation + + public struct SourceSpan { + public readonly SourceLocation Location; + public readonly int Length; + public SourceSpan(SourceLocation location, int length) { + Location = location; + Length = length; + } + public int EndPosition { + get { return Location.Position + Length; } + } + public bool InRange(int position) { + return (position >= Location.Position && position <= EndPosition); + } + + } + + +}//namespace diff --git a/Irony/Parsing/Scanner/SourceStream.cs b/Irony/Parsing/Scanner/SourceStream.cs new file mode 100644 index 0000000..e80abf6 --- /dev/null +++ b/Irony/Parsing/Scanner/SourceStream.cs @@ -0,0 +1,156 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + public class SourceStream : ISourceStream { + StringComparison _stringComparison; + int _tabWidth; + char[] _chars; + int _textLength; + + public SourceStream(string text, bool caseSensitive, int tabWidth) : this(text, caseSensitive, tabWidth, new SourceLocation()) { + } + + public SourceStream(string text, bool caseSensitive, int tabWidth, SourceLocation initialLocation) { + _text = text; + _textLength = _text.Length; + _chars = Text.ToCharArray(); + _stringComparison = caseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; + _tabWidth = tabWidth; + _location = initialLocation; + _previewPosition = _location.Position; + if (_tabWidth <= 1) + _tabWidth = 8; + } + + #region ISourceStream Members + public string Text { + get { return _text; } + } string _text; + + public int Position { + get { return _location.Position; } + set { + if (_location.Position != value) + SetNewPosition(value); + } + } + + public SourceLocation Location { + [System.Diagnostics.DebuggerStepThrough] + get { return _location; } + set { _location = value; } + } SourceLocation _location; + + public int PreviewPosition { + get { return _previewPosition; } + set { _previewPosition = value; } + } int _previewPosition; + + public char PreviewChar { + [System.Diagnostics.DebuggerStepThrough] + get { + if (_previewPosition >= _textLength) + return '\0'; + return _chars[_previewPosition]; + } + } + + public char NextPreviewChar { + [System.Diagnostics.DebuggerStepThrough] + get { + if (_previewPosition + 1 >= _textLength) return '\0'; + return _chars[_previewPosition + 1]; + } + } + + public bool MatchSymbol(string symbol) { + try { + int cmp = string.Compare(_text, PreviewPosition, symbol, 0, symbol.Length, _stringComparison); + return cmp == 0; + } catch { + //exception may be thrown if Position + symbol.length > text.Length; + // this happens not often, only at the very end of the file, so we don't check this explicitly + //but simply catch the exception and return false. Again, try/catch block has no overhead + // if exception is not thrown. + return false; + } + } + + public Token CreateToken(Terminal terminal) { + var tokenText = GetPreviewText(); + return new Token(terminal, this.Location, tokenText, tokenText); + } + public Token CreateToken(Terminal terminal, object value) { + var tokenText = GetPreviewText(); + return new Token(terminal, this.Location, tokenText, value); + } + + [System.Diagnostics.DebuggerStepThrough] + public bool EOF() { + return _previewPosition >= _textLength; + } + #endregion + + //returns substring from Location.Position till (PreviewPosition - 1) + private string GetPreviewText() { + var until = _previewPosition; + if (until > _textLength) until = _textLength; + var p = _location.Position; + string text = Text.Substring(p, until - p); + return text; + } + + // To make debugging easier: show 20 chars from current position + public override string ToString() { + string result; + try { + var p = Location.Position; + if (p + 20 < _textLength) + result = _text.Substring(p, 20) + Resources.LabelSrcHaveMore;// " ..." + else + result = _text.Substring(p) + Resources.LabelEofMark; //"(EOF)" + } catch (Exception) { + result = PreviewChar + Resources.LabelSrcHaveMore; + } + return string.Format(Resources.MsgSrcPosToString , result, Location); //"[{0}], at {1}" + } + + //Computes the Location info (line, col) for a new source position. + private void SetNewPosition(int newPosition) { + if (newPosition < Position) + throw new Exception(Resources.ErrCannotMoveBackInSource); + int p = Position; + int col = Location.Column; + int line = Location.Line; + while(p < newPosition) { + var curr = _chars[p]; + switch (curr) { + case '\n': line++; col = 0; break; + case '\r': break; + case '\t': col = (col / _tabWidth + 1) * _tabWidth; break; + default: col++; break; + } //switch + p++; + } + Location = new SourceLocation(p, line, col); + } + + + }//class + +}//namespace diff --git a/Irony/Parsing/Scanner/Token.cs b/Irony/Parsing/Scanner/Token.cs new file mode 100644 index 0000000..1fdd281 --- /dev/null +++ b/Irony/Parsing/Scanner/Token.cs @@ -0,0 +1,99 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace Irony.Parsing { + + public enum TokenFlags { + IsIncomplete = 0x01, + } + + public enum TokenCategory { + Content, + Outline, //newLine, indent, dedent + Comment, + Directive, + Error, + } + + public class TokenList : List {} + public class TokenStack : Stack { } + + //Tokens are produced by scanner and fed to parser, optionally passing through Token filters in between. + public class Token { + public Terminal Terminal {get; private set;} + public KeyTerm KeyTerm; + public readonly SourceLocation Location; + public readonly string Text; + + public object Value; + public string ValueString { + get { return (Value == null ? string.Empty : Value.ToString()); } + } + + public object Details; + public TokenFlags Flags; + public TokenEditorInfo EditorInfo; + + public Token(Terminal term, SourceLocation location, string text, object value) { + SetTerminal(term); + this.KeyTerm = term as KeyTerm; + Location = location; + Text = text; + Value = value; + } + + public void SetTerminal(Terminal terminal) { + Terminal = terminal; + this.EditorInfo = Terminal.EditorInfo; //set to term's EditorInfo by default + } + + public bool IsSet(TokenFlags flag) { + return (Flags & flag) != 0; + } + public TokenCategory Category { + get { return Terminal.Category; } + } + + public bool IsError() { + return Category == TokenCategory.Error; + } + + public int Length { + get { return Text == null ? 0 : Text.Length; } + } + + //matching opening/closing brace + public Token OtherBrace; + + public short ScannerState; //Scanner state after producing token + + [System.Diagnostics.DebuggerStepThrough] + public override string ToString() { + return Terminal.TokenToString(this); + }//method + + }//class + + //Some terminals may need to return a bunch of tokens in one call to TryMatch; MultiToken is a container for these tokens + public class MultiToken : Token { + public TokenList ChildTokens; + public MultiToken(Terminal term, SourceLocation location, TokenList childTokens) : base(term, location, string.Empty, null) { + ChildTokens = childTokens; + } + }//class + +}//namespace diff --git a/Irony/Parsing/Scanner/TokenEditorInfo.cs b/Irony/Parsing/Scanner/TokenEditorInfo.cs new file mode 100644 index 0000000..c3e518d --- /dev/null +++ b/Irony/Parsing/Scanner/TokenEditorInfo.cs @@ -0,0 +1,108 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + // Helper classes for information used by syntax highlighters and editors + // TokenColor, TokenTriggers and TokenType are copied from the Visual studio integration assemblies. + // Each terminal/token would have its TokenEditorInfo that can be used either by VS integration package + // or any editor for syntax highligting. + + public class TokenEditorInfo { + public readonly TokenType Type; + public readonly TokenColor Color; + public readonly TokenTriggers Triggers; + public string ToolTip; + public int UnderlineType; + public TokenEditorInfo(TokenType type, TokenColor color, TokenTriggers triggers) { + Type = type; + Color = color; + Triggers = triggers; + } + + }//class + + public enum TokenColor { + Text = 0, + Keyword = 1, + Comment = 2, + Identifier = 3, + String = 4, + Number = 5, + } + + // (Comments are coming from visual studio integration package) + // Specifies a set of triggers that can be fired from an Microsoft.VisualStudio.Package.IScanner + // language parser. + [Flags] + public enum TokenTriggers { + // Summary: + // Used when no triggers are set. This is the default. + None = 0, + // + // Summary: + // A character that indicates that the start of a member selection has been + // parsed. In C#, this could be a period following a class name. In XML, this + // could be a < (the member select is a list of possible tags). + MemberSelect = 1, + // + // Summary: + // The opening or closing part of a language pair has been parsed. For example, + // in C#, a { or } has been parsed. In XML, a < or > has been parsed. + MatchBraces = 2, + // + // Summary: + // A character that marks the start of a parameter list has been parsed. For + // example, in C#, this could be an open parenthesis, "(". + ParameterStart = 16, + // + // Summary: + // A character that separates parameters in a list has been parsed. For example, + // in C#, this could be a comma, ",". + ParameterNext = 32, + // + // Summary: + // A character that marks the end of a parameter list has been parsed. For example, + // in C#, this could be a close parenthesis, ")". + ParameterEnd = 64, + // + // Summary: + // A parameter in a method's parameter list has been parsed. + Parameter = 128, + // + // Summary: + // This is a mask for the flags used to govern the IntelliSense Method Tip operation. + // This mask is used to isolate the values Microsoft.VisualStudio.Package.TokenTriggers.Parameter, + // Microsoft.VisualStudio.Package.TokenTriggers.ParameterStart, Microsoft.VisualStudio.Package.TokenTriggers.ParameterNext, + // and Microsoft.VisualStudio.Package.TokenTriggers.ParameterEnd. + MethodTip = 240, + } + + public enum TokenType { + Unknown = 0, + Text = 1, + Keyword = 2, + Identifier = 3, + String = 4, + Literal = 5, + Operator = 6, + Delimiter = 7, + WhiteSpace = 8, + LineComment = 9, + Comment = 10, + } + +} diff --git a/Irony/Parsing/Scanner/_ISourceStream.cs b/Irony/Parsing/Scanner/_ISourceStream.cs new file mode 100644 index 0000000..7f4ada8 --- /dev/null +++ b/Irony/Parsing/Scanner/_ISourceStream.cs @@ -0,0 +1,86 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + // + /// + /// Interface for Terminals to access the source stream and produce tokens. + /// + public interface ISourceStream { + + /// + /// Returns the source text + /// + string Text { get; } + + /// + /// Gets or sets the start location (position, row, column) of the new token + /// + SourceLocation Location { get; set; } + + /// + /// Gets or sets the current position in the source file. When reading the value, returns Location.Position value. + /// When a new value is assigned, the Location is modified accordingly. + /// + int Position { get; set; } + + /// + /// Gets or sets the current preview position in the source file. Must be greater or equal to Location.Position + /// + int PreviewPosition { get; set; } + /// + /// Gets a char at preview position + /// + char PreviewChar { get; } + /// + /// Gets the char at position next after the PrevewPosition + /// + char NextPreviewChar { get; } //char at PreviewPosition+1 + + /// + /// Creates a new token based on current preview position. + /// + /// A terminal associated with the token. + /// New token. + Token CreateToken(Terminal terminal); + + /// + /// Creates a new token based on current preview position and sets its Value field. + /// + /// A terminal associated with the token. + /// The value associated with the token. + /// New token. + Token CreateToken(Terminal terminal, object value); + + /// Tries to match the symbol with the text at current preview position. + /// A symbol to match + /// True if there is a match; otherwise, false. + bool MatchSymbol(string symbol); + + bool EOF(); + + /* + //This member is intentionally removed from ISourceStream and made private in SourceStream class. The purpose is to discourage + its use or imitation - it produces a new string object which means new garbage for GC. All Irony-defined Terminal classes + are implemented without it, but you can always reproduce the implementation in your custom code if you really need it + string GetPreviewText(); + */ + + }//interface + + +} diff --git a/Irony/Parsing/Terminals/CommentTerminal.cs b/Irony/Parsing/Terminals/CommentTerminal.cs new file mode 100644 index 0000000..f1a0b6d --- /dev/null +++ b/Irony/Parsing/Terminals/CommentTerminal.cs @@ -0,0 +1,119 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + public class CommentTerminal : Terminal { + public CommentTerminal(string name, string startSymbol, params string[] endSymbols) : base(name, TokenCategory.Comment) { + this.StartSymbol = startSymbol; + this.EndSymbols = new StringList(); + EndSymbols.AddRange(endSymbols); + Priority = TerminalPriority.High; //assign max priority + } + + public string StartSymbol; + public StringList EndSymbols; + private char[] _endSymbolsFirsts; + private bool _isLineComment; //true if NewLine is one of EndSymbols; if yes, EOF is also considered a valid end symbol + + + #region overrides + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + //_endSymbolsFirsts char array is used for fast search for end symbols using String's method IndexOfAny(...) + _endSymbolsFirsts = new char[EndSymbols.Count]; + for (int i = 0; i < EndSymbols.Count; i++) { + string sym = EndSymbols[i]; + _endSymbolsFirsts[i] = sym[0]; + _isLineComment |= sym.Contains("\n"); + if (!_isLineComment) + SetFlag(TermFlags.IsMultiline); + } + if (this.EditorInfo == null) { + TokenType ttype = _isLineComment ? TokenType.LineComment : TokenType.Comment; + this.EditorInfo = new TokenEditorInfo(ttype, TokenColor.Comment, TokenTriggers.None); + } + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + Token result; + if (context.VsLineScanState.Value != 0) { + // we are continuing in line mode - restore internal env (none in this case) + context.VsLineScanState.Value = 0; + } else { + //we are starting from scratch + if (!BeginMatch(context, source)) return null; + } + result = CompleteMatch(context, source); + if (result != null) return result; + //if it is LineComment, it is ok to hit EOF without final line-break; just return all until end. + if (_isLineComment) + return source.CreateToken(this.OutputTerminal); + if (context.Mode == ParseMode.VsLineScan) + return CreateIncompleteToken(context, source); + return context.CreateErrorToken(Resources.ErrUnclosedComment); + } + + private Token CreateIncompleteToken(ParsingContext context, ISourceStream source) { + source.PreviewPosition = source.Text.Length; + Token result = source.CreateToken(this.OutputTerminal); + result.Flags |= TokenFlags.IsIncomplete; + context.VsLineScanState.TerminalIndex = this.MultilineIndex; + return result; + } + + private bool BeginMatch(ParsingContext context, ISourceStream source) { + //Check starting symbol + if (!source.MatchSymbol(StartSymbol)) return false; + source.PreviewPosition += StartSymbol.Length; + return true; + } + private Token CompleteMatch(ParsingContext context, ISourceStream source) { + //Find end symbol + while (!source.EOF()) { + int firstCharPos; + if (EndSymbols.Count == 1) + firstCharPos = source.Text.IndexOf(EndSymbols[0], source.PreviewPosition); + else + firstCharPos = source.Text.IndexOfAny(_endSymbolsFirsts, source.PreviewPosition); + if (firstCharPos < 0) { + source.PreviewPosition = source.Text.Length; + return null; //indicating error + } + //We found a character that might start an end symbol; let's see if it is true. + source.PreviewPosition = firstCharPos; + foreach (string endSymbol in EndSymbols) { + if (source.MatchSymbol(endSymbol)) { + //We found end symbol; eat end symbol only if it is not line comment. + // For line comment, leave LF symbol there, it might be important to have a separate LF token + if (!_isLineComment) + source.PreviewPosition += endSymbol.Length; + return source.CreateToken(this.OutputTerminal); + }//if + }//foreach endSymbol + source.PreviewPosition++; //move to the next char and try again + }//while + return null; //might happen if we found a start char of end symbol, but not the full endSymbol + }//method + + public override IList GetFirsts() { + return new string[] { StartSymbol }; + } + #endregion + }//CommentTerminal class + + +} diff --git a/Irony/Parsing/Terminals/CompoundTerminalBase.cs b/Irony/Parsing/Terminals/CompoundTerminalBase.cs new file mode 100644 index 0000000..05c69fc --- /dev/null +++ b/Irony/Parsing/Terminals/CompoundTerminalBase.cs @@ -0,0 +1,261 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + #region About compound terminals + /* + As it turns out, many terminal types in real-world languages have 3-part structure: prefix-body-suffix + The body is essentially the terminal "value", while prefix and suffix are used to specify additional + information (options), while not being a part of the terminal itself. + For example: + 1. c# numbers, may have 0x prefix for hex representation, and suffixes specifying + the exact data type of the literal (f, l, m, etc) + 2. c# string may have "@" prefix which disables escaping inside the string + 3. c# identifiers may have "@" prefix and escape sequences inside - just like strings + 4. Python string may have "u" and "r" prefixes, "r" working the same way as @ in c# strings + 5. VB string literals may have "c" suffix identifying that the literal is a character, not a string + 6. VB number literals and identifiers may have suffixes identifying data type + + So it seems like all these terminals have the format "prefix-body-suffix". + The CompoundTerminalBase base class implements base functionality supporting this multi-part structure. + The IdentifierTerminal, NumberLiteral and StringLiteral classes inherit from this base class. + The methods in TerminalFactory static class demonstrate that with this architecture we can define the whole + variety of terminals for c#, Python and VB.NET languages. +*/ + #endregion + + + public class EscapeTable : Dictionary { } + + public abstract class CompoundTerminalBase : Terminal { + + #region Nested classes + protected class ScanFlagTable : Dictionary { } + protected class TypeCodeTable : Dictionary { } + + public class CompoundTokenDetails { + public string Prefix; + public string Body; + public string Suffix; + public string Sign; + public short Flags; //need to be short, because we need to save it in Scanner state for Vs integration + public string Error; + public TypeCode[] TypeCodes; + public string ExponentSymbol; //exponent symbol for Number literal + public string StartSymbol; //string start and end symbols + public string EndSymbol; + public object Value; + //partial token info, used by VS integration + public bool PartialOk; + public bool IsPartial; + public bool PartialContinues; + public byte SubTypeIndex; //used for string literal kind + //Flags helper method + public bool IsSet(short flag) { + return (Flags & flag) != 0; + } + public string Text { get { return Prefix + Body + Suffix; } } + } + + #endregion + + #region constructors and initialization + public CompoundTerminalBase(string name) : this(name, TermFlags.None) { } + public CompoundTerminalBase(string name, TermFlags flags) : base(name) { + SetFlag(flags); + Escapes = GetDefaultEscapes(); + } + + protected void AddPrefixFlag(string prefix, short flags) { + PrefixFlags.Add(prefix, flags); + Prefixes.Add(prefix); + } + public void AddSuffix(string suffix, params TypeCode[] typeCodes) { + SuffixTypeCodes.Add(suffix, typeCodes); + Suffixes.Add(suffix); + } + #endregion + + #region public Properties/Fields + public Char EscapeChar = '\\'; + public EscapeTable Escapes = new EscapeTable(); + //Case sensitivity for prefixes and suffixes + public bool CaseSensitivePrefixesSuffixes = false; + #endregion + + + #region private fields + protected readonly ScanFlagTable PrefixFlags = new ScanFlagTable(); + protected readonly TypeCodeTable SuffixTypeCodes = new TypeCodeTable(); + protected StringList Prefixes = new StringList(); + protected StringList Suffixes = new StringList(); + CharHashSet _prefixesFirsts; //first chars of all prefixes, for fast prefix detection + CharHashSet _suffixesFirsts; //first chars of all suffixes, for fast suffix detection + #endregion + + + #region overrides: Init, TryMatch + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + //collect all suffixes, prefixes in lists and create sets of first chars for both + Prefixes.Sort(StringList.LongerFirst); + Suffixes.Sort(StringList.LongerFirst); + + _prefixesFirsts = new CharHashSet(CaseSensitivePrefixesSuffixes); + _suffixesFirsts = new CharHashSet(CaseSensitivePrefixesSuffixes); + foreach (string pfx in Prefixes) + _prefixesFirsts.Add(pfx[0]); + + foreach (string sfx in Suffixes) + _suffixesFirsts.Add(sfx[0]); + }//method + + public override IList GetFirsts() { + return Prefixes; + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + Token token; + //Try quick parse first, but only if we're not continuing + if (context.VsLineScanState.Value == 0) { + token = QuickParse(context, source); + if (token != null) return token; + source.PreviewPosition = source.Location.Position; //revert the position + } + + CompoundTokenDetails details = new CompoundTokenDetails(); + InitDetails(context, details); + + if (context.VsLineScanState.Value == 0) + ReadPrefix(source, details); + if (!ReadBody(source, details)) + return null; + if (details.Error != null) + return context.CreateErrorToken(details.Error); + if (details.IsPartial) { + details.Value = details.Body; + } else { + ReadSuffix(source, details); + + if(!ConvertValue(details)) { + if (string.IsNullOrEmpty(details.Error)) + details.Error = Resources.ErrInvNumber; + return context.CreateErrorToken(details.Error); // "Failed to convert the value: {0}" + } + } + token = CreateToken(context, source, details); + + if (details.IsPartial) { + //Save terminal state so we can continue + context.VsLineScanState.TokenSubType = (byte)details.SubTypeIndex; + context.VsLineScanState.TerminalFlags = (short)details.Flags; + context.VsLineScanState.TerminalIndex = this.MultilineIndex; + } else + context.VsLineScanState.Value = 0; + return token; + } + + protected virtual Token CreateToken(ParsingContext context, ISourceStream source, CompoundTokenDetails details) { + var token = source.CreateToken(this.OutputTerminal, details.Value); + token.Details = details; + if (details.IsPartial) + token.Flags |= TokenFlags.IsIncomplete; + return token; + } + + protected virtual void InitDetails(ParsingContext context, CompoundTokenDetails details) { + details.PartialOk = (context.Mode == ParseMode.VsLineScan); + details.PartialContinues = (context.VsLineScanState.Value != 0); + } + + protected virtual Token QuickParse(ParsingContext context, ISourceStream source) { + return null; + } + + protected virtual void ReadPrefix(ISourceStream source, CompoundTokenDetails details) { + if (!_prefixesFirsts.Contains(source.PreviewChar)) + return; + var comparisonType = CaseSensitivePrefixesSuffixes ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; + foreach (string pfx in Prefixes) { + // Prefixes are usually case insensitive, even if language is case-sensitive. So we cannot use source.MatchSymbol here, + // we need case-specific comparison + if (string.Compare(source.Text, source.PreviewPosition, pfx, 0, pfx.Length, comparisonType) != 0) + continue; + //We found prefix + details.Prefix = pfx; + source.PreviewPosition += pfx.Length; + //Set flag from prefix + short pfxFlags; + if (!string.IsNullOrEmpty(details.Prefix) && PrefixFlags.TryGetValue(details.Prefix, out pfxFlags)) + details.Flags |= (short) pfxFlags; + return; + }//foreach + }//method + + protected virtual bool ReadBody(ISourceStream source, CompoundTokenDetails details) { + return false; + } + + protected virtual void ReadSuffix(ISourceStream source, CompoundTokenDetails details) { + if (!_suffixesFirsts.Contains(source.PreviewChar)) return; + var comparisonType = CaseSensitivePrefixesSuffixes ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; + foreach (string sfx in Suffixes) { + //Suffixes are usually case insensitive, even if language is case-sensitive. So we cannot use source.MatchSymbol here, + // we need case-specific comparison + if (string.Compare(source.Text, source.PreviewPosition, sfx, 0, sfx.Length, comparisonType) != 0) + continue; + //We found suffix + details.Suffix = sfx; + source.PreviewPosition += sfx.Length; + //Set TypeCode from suffix + TypeCode[] codes; + if (!string.IsNullOrEmpty(details.Suffix) && SuffixTypeCodes.TryGetValue(details.Suffix, out codes)) + details.TypeCodes = codes; + return; + }//foreach + }//method + + protected virtual bool ConvertValue(CompoundTokenDetails details) { + details.Value = details.Body; + return false; + } + + + #endregion + + #region utils: GetDefaultEscapes + public static EscapeTable GetDefaultEscapes() { + EscapeTable escapes = new EscapeTable(); + escapes.Add('a', '\u0007'); + escapes.Add('b', '\b'); + escapes.Add('t', '\t'); + escapes.Add('n', '\n'); + escapes.Add('v', '\v'); + escapes.Add('f', '\f'); + escapes.Add('r', '\r'); + escapes.Add('"', '"'); + escapes.Add('\'', '\''); + escapes.Add('\\', '\\'); + escapes.Add(' ', ' '); + escapes.Add('\n', '\n'); //this is a special escape of the linebreak itself, + // when string ends with "\" char and continues on the next line + return escapes; + } + #endregion + + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/ConstantTerminal.cs b/Irony/Parsing/Terminals/ConstantTerminal.cs new file mode 100644 index 0000000..f07f8d9 --- /dev/null +++ b/Irony/Parsing/Terminals/ConstantTerminal.cs @@ -0,0 +1,61 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + //This terminal allows to declare a set of constants in the input language + // It should be used when constant symbols do not look like normal identifiers; e.g. in Scheme, #t, #f are true/false + // constants, and they don't fit into Scheme identifier pattern. + public class ConstantsTable : Dictionary { } + public class ConstantTerminal : Terminal { + public readonly ConstantsTable Constants = new ConstantsTable(); + public ConstantTerminal(string name, Type nodeType) : base(name) { + base.SetFlag(TermFlags.IsConstant); + base.AstConfig.NodeType = nodeType; + } + + public void Add(string lexeme, object value) { + this.Constants[lexeme] = value; + } + + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + if (this.EditorInfo == null) + this.EditorInfo = new TokenEditorInfo(TokenType.Unknown, TokenColor.Text, TokenTriggers.None); + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + string text = source.Text; + foreach (var entry in Constants) { + var constant = entry.Key; + if (source.PreviewPosition + constant.Length > text.Length) continue; + if (source.MatchSymbol(constant)) { + source.PreviewPosition += constant.Length; + return source.CreateToken(this.OutputTerminal, entry.Value); + } + } + return null; + } + public override IList GetFirsts() { + string[] array = new string[Constants.Count]; + Constants.Keys.CopyTo(array, 0); + return array; + } + + }//class + + + +} diff --git a/Irony/Parsing/Terminals/CustomTerminal.cs b/Irony/Parsing/Terminals/CustomTerminal.cs new file mode 100644 index 0000000..7f9e908 --- /dev/null +++ b/Irony/Parsing/Terminals/CustomTerminal.cs @@ -0,0 +1,46 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + //Terminal based on custom method; allows creating custom match without creating new class derived from Terminal + public delegate Token MatchHandler(Terminal terminal, ParsingContext context, ISourceStream source); + + public class CustomTerminal : Terminal { + public CustomTerminal(string name, MatchHandler handler, params string[] prefixes) : base(name) { + _handler = handler; + if (prefixes != null) + Prefixes.AddRange(prefixes); + this.EditorInfo = new TokenEditorInfo(TokenType.Unknown, TokenColor.Text, TokenTriggers.None); + } + + public readonly StringList Prefixes = new StringList(); + + public MatchHandler Handler { + [System.Diagnostics.DebuggerStepThrough] + get {return _handler;} + } MatchHandler _handler; + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + return _handler(this, context, source); + } + [System.Diagnostics.DebuggerStepThrough] + public override IList GetFirsts() { + return Prefixes; + } + }//class + + +} diff --git a/Irony/Parsing/Terminals/DataLiteralBase.cs b/Irony/Parsing/Terminals/DataLiteralBase.cs new file mode 100644 index 0000000..3d0f978 --- /dev/null +++ b/Irony/Parsing/Terminals/DataLiteralBase.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; + +namespace Irony.Parsing { + + //DataLiteralBase is a base class for a set of specialized terminals with a primary purpose of building data readers + // DsvLiteral is used for reading delimiter-separated values (DSV), comma-separated format is a specific case of DSV + // FixedLengthLiteral may be used to read values of fixed length + public class DataLiteralBase : Terminal { + public TypeCode DataType; + //For date format strings see MSDN help for "Custom format strings", available through help for DateTime.ParseExact(...) method + public string DateTimeFormat = "d"; //standard format, identifies MM/dd/yyyy for invariant culture. + public int IntRadix = 10; //Radix (base) for numeric numbers + + public DataLiteralBase(string name, TypeCode dataType) : base(name) { + DataType = dataType; + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + try { + var textValue = ReadBody(context, source); + if (textValue == null) return null; + var value = ConvertValue(context, textValue); + return source.CreateToken(this.OutputTerminal, value); + } catch(Exception ex) { + //we throw exception in DsvLiteral when we cannot find a closing quote for quoted value + return context.CreateErrorToken(ex.Message); + } + }//method + + + protected virtual string ReadBody(ParsingContext context, ISourceStream source) { + return null; + } + + protected virtual object ConvertValue(ParsingContext context, string textValue) { + switch(DataType) { + case TypeCode.String: return textValue; + case TypeCode.DateTime: return DateTime.ParseExact(textValue, DateTimeFormat, context.Culture); + case TypeCode.Single: + case TypeCode.Double: + var dValue = Convert.ToDouble(textValue, context.Culture); + if (DataType == TypeCode.Double) return dValue; + return Convert.ChangeType(dValue, DataType, context.Culture); + + default: //integer types + var iValue = (IntRadix == 10)? Convert.ToInt64(textValue, context.Culture) : Convert.ToInt64(textValue, IntRadix); + if (DataType == TypeCode.Int64) return iValue; + return Convert.ChangeType(iValue, DataType, context.Culture); + } + }//method + + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/DsvLiteral.cs b/Irony/Parsing/Terminals/DsvLiteral.cs new file mode 100644 index 0000000..8ade879 --- /dev/null +++ b/Irony/Parsing/Terminals/DsvLiteral.cs @@ -0,0 +1,105 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + //A terminal for DSV-formatted files (Delimiter-Separated Values), a generalization of CSV (comma-separated values) format. + // See http://en.wikipedia.org/wiki/Delimiter-separated_values + // For CSV format, there's a recommendation RFC4180 (http://tools.ietf.org/html/rfc4180) + // It might seem that this terminal is not that useful and it is easy enough to create a custom CSV reader for a particular data format + // format. However, if you consider all escaping and double-quote enclosing rules, then a custom reader solution would not seem so trivial. + // So DsvLiteral can simplify this task. + public class DsvLiteral : DataLiteralBase { + public string Terminator = ","; + public bool ConsumeTerminator = true; //if true, the source pointer moves after the separator + private char[] _terminators; + + //For last value on the line specify terminator = null; the DsvLiteral will then look for NewLine as terminator + public DsvLiteral(string name, TypeCode dataType, string terminator) : this(name, dataType) { + Terminator = terminator; + } + public DsvLiteral(string name, TypeCode dataType) : base(name, dataType) { } + + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + if (Terminator == null) + _terminators = new char[] { '\n', '\r'}; + else + _terminators = new char[] { Terminator[0]}; + } + + protected override string ReadBody(ParsingContext context, ISourceStream source) { + string body; + if (source.PreviewChar == '"') + body = ReadQuotedBody(context, source); + else + body = ReadNotQuotedBody(context, source); + if (ConsumeTerminator && Terminator != null) + MoveSourcePositionAfterTerminator(source); + return body; + } + + private string ReadQuotedBody(ParsingContext context, ISourceStream source) { + const char dQuoute = '"'; + StringBuilder sb = null; + var from = source.Location.Position + 1; //skip initial double quote + while(true) { + var until = source.Text.IndexOf(dQuoute, from); + if (until < 0) + throw new Exception(Resources.ErrDsvNoClosingQuote); // "Could not find a closing quote for quoted value." + source.PreviewPosition = until; //now points at double-quote + var piece = source.Text.Substring(from, until - from); + source.PreviewPosition++; //move after double quote + if (source.PreviewChar != dQuoute && sb == null) + return piece; //quick path - if sb (string builder) was not created yet, we are looking at the very first segment; + // and if we found a standalone dquote, then we are done - the "piece" is the result. + if (sb == null) + sb = new StringBuilder(100); + sb.Append(piece); + if (source.PreviewChar != dQuoute) + return sb.ToString(); + //we have doubled double-quote; add a single double-quoute char to the result and move over both symbols + sb.Append(dQuoute); + from = source.PreviewPosition + 1; + } + } + + private string ReadNotQuotedBody(ParsingContext context, ISourceStream source) { + var startPos = source.Location.Position; + var sepPos = source.Text.IndexOfAny(_terminators, startPos); + if (sepPos < 0) + sepPos = source.Text.Length; + source.PreviewPosition = sepPos; + var valueText = source.Text.Substring(startPos, sepPos - startPos); + return valueText; + } + + private void MoveSourcePositionAfterTerminator(ISourceStream source) { + while(!source.EOF()) { + while(source.PreviewChar != Terminator[0]) + source.PreviewPosition++; + if(source.MatchSymbol(Terminator)) { + source.PreviewPosition += Terminator.Length; + return; + }//if + }//while + }//method + + }//class + + +}//namespace diff --git a/Irony/Parsing/Terminals/FixedLengthLiteral.cs b/Irony/Parsing/Terminals/FixedLengthLiteral.cs new file mode 100644 index 0000000..ab47fa7 --- /dev/null +++ b/Irony/Parsing/Terminals/FixedLengthLiteral.cs @@ -0,0 +1,39 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; + +namespace Irony.Parsing { + + //A terminal for representing fixed-length lexemes coming up sometimes in programming language + // (in Fortran for ex, every line starts with 5-char label, followed by a single continuation char) + // It may be also used to create grammar/parser for reading data files with fixed length fields + public class FixedLengthLiteral : DataLiteralBase { + public int Length; + + public FixedLengthLiteral(string name, int length, TypeCode dataType) : base(name, dataType) { + Length = length; + } + + protected override string ReadBody(ParsingContext context, ISourceStream source) { + source.PreviewPosition = source.Location.Position + Length; + var body = source.Text.Substring(source.Location.Position, Length); + return body; + } + + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/FreeTextLiteral.cs b/Irony/Parsing/Terminals/FreeTextLiteral.cs new file mode 100644 index 0000000..bfe0770 --- /dev/null +++ b/Irony/Parsing/Terminals/FreeTextLiteral.cs @@ -0,0 +1,157 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + // Sometimes language definition includes tokens that have no specific format, but are just "all text until some terminator character(s)"; + // FreeTextTerminal allows easy implementation of such language element. + + [Flags] + public enum FreeTextOptions { + None = 0x0, + ConsumeTerminator = 0x01, //move source pointer beyond terminator (so token "consumes" it from input), but don't include it in token text + IncludeTerminator = 0x02, // include terminator into token text/value + AllowEof = 0x04, // treat EOF as legitimate terminator + } + + public class FreeTextLiteral : Terminal { + public StringSet Terminators = new StringSet(); + public StringSet Firsts = new StringSet(); + public StringDictionary Escapes = new StringDictionary(); + public FreeTextOptions FreeTextOptions; + private char[] _stopChars; + bool _isSimple; //True if we have a single Terminator and no escapes + string _singleTerminator; + + public FreeTextLiteral(string name, params string[] terminators) : this(name, FreeTextOptions.None, terminators) { } + public FreeTextLiteral(string name, FreeTextOptions freeTextOptions, params string[] terminators) : base(name) { + FreeTextOptions = freeTextOptions; + Terminators.UnionWith(terminators); + base.SetFlag(TermFlags.IsLiteral); + }//constructor + + public override IList GetFirsts() { + var result = new StringList(); + result.AddRange(Firsts); + return result; + } + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + _isSimple = Terminators.Count == 1 && Escapes.Count == 0; + if (_isSimple) { + _singleTerminator = Terminators.First(); + return; + } + var stopChars = new CharHashSet(); + foreach (var key in Escapes.Keys) + stopChars.Add(key[0]); + foreach (var t in Terminators) + stopChars.Add(t[0]); + _stopChars = stopChars.ToArray(); + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + if (!TryMatchPrefixes(context, source)) + return null; + return _isSimple ? TryMatchContentSimple(context, source) : TryMatchContentExtended(context, source); + } + + private bool TryMatchPrefixes(ParsingContext context, ISourceStream source) { + if (Firsts.Count == 0) + return true; + foreach (var first in Firsts) + if (source.MatchSymbol(first)) { + source.PreviewPosition += first.Length; + return true; + } + return false; + } + + private Token TryMatchContentSimple(ParsingContext context, ISourceStream source) { + var startPos = source.PreviewPosition; + var termLen = _singleTerminator.Length; + var stringComp = Grammar.CaseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; + int termPos = source.Text.IndexOf(_singleTerminator, startPos, stringComp); + if (termPos < 0 && IsSet(FreeTextOptions.AllowEof)) + termPos = source.Text.Length; + if (termPos < 0) + return context.CreateErrorToken(Resources.ErrFreeTextNoEndTag, _singleTerminator); + var textEnd = termPos; + if (IsSet(FreeTextOptions.IncludeTerminator)) + textEnd += termLen; + var tokenText = source.Text.Substring(startPos, textEnd - startPos); + // The following line is a fix submitted by user rmcase + source.PreviewPosition = IsSet(FreeTextOptions.ConsumeTerminator) ? termPos + termLen : termPos; + return source.CreateToken(this.OutputTerminal, tokenText); + } + + private Token TryMatchContentExtended(ParsingContext context, ISourceStream source) { + StringBuilder tokenText = new StringBuilder(); + while (true) { + //Find next position of one of stop chars + var nextPos = source.Text.IndexOfAny(_stopChars, source.PreviewPosition); + if(nextPos == -1) { + if(IsSet(FreeTextOptions.AllowEof)) { + source.PreviewPosition = source.Text.Length; + return source.CreateToken(this.OutputTerminal); + } else + return null; + } + var newText = source.Text.Substring(source.PreviewPosition, nextPos - source.PreviewPosition); + tokenText.Append(newText); + source.PreviewPosition = nextPos; + //if it is escape, add escaped text and continue search + if (CheckEscape(source, tokenText)) + continue; + //check terminators + if (CheckTerminators(source, tokenText)) + break; //from while (true); we reached + //The current stop is not at escape or terminator; add this char to token text and move on + tokenText.Append(source.PreviewChar); + source.PreviewPosition++; + }//while + return source.CreateToken(this.OutputTerminal, tokenText.ToString()); + } + + private bool CheckEscape(ISourceStream source, StringBuilder tokenText) { + foreach (var dictEntry in Escapes) { + if (source.MatchSymbol(dictEntry.Key)) { + source.PreviewPosition += dictEntry.Key.Length; + tokenText.Append(dictEntry.Value); + return true; + } + }//foreach + return false; + } + + private bool CheckTerminators(ISourceStream source, StringBuilder tokenText) { + foreach(var term in Terminators) + if(source.MatchSymbol(term)) { + if (IsSet(FreeTextOptions.IncludeTerminator)) + tokenText.Append(term); + if (IsSet(FreeTextOptions.ConsumeTerminator | FreeTextOptions.IncludeTerminator)) + source.PreviewPosition += term.Length; + return true; + } + return false; + } + + private bool IsSet(FreeTextOptions option) { + return (this.FreeTextOptions & option) != 0; + } + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/IdentifierTerminal.cs b/Irony/Parsing/Terminals/IdentifierTerminal.cs new file mode 100644 index 0000000..f0f6928 --- /dev/null +++ b/Irony/Parsing/Terminals/IdentifierTerminal.cs @@ -0,0 +1,266 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Globalization; + +namespace Irony.Parsing { + #region notes + //Identifier terminal. Matches alpha-numeric sequences that usually represent identifiers and keywords. + // c#: @ prefix signals to not interpret as a keyword; allows \u escapes + // + + #endregion + + [Flags] + public enum IdOptions : short { + None = 0, + AllowsEscapes = 0x01, + CanStartWithEscape = 0x03, //bit 2 with bit 1 together + + IsNotKeyword = 0x10, + NameIncludesPrefix = 0x20, + } + + public enum CaseRestriction { + None, + FirstUpper, + FirstLower, + AllUpper, + AllLower + } + + public class UnicodeCategoryList : List { } + + public class IdentifierTerminal : CompoundTerminalBase { + + //Id flags for internal use + internal enum IdFlagsInternal : short { + HasEscapes = 0x100, + } + + + #region constructors and initialization + public IdentifierTerminal(string name) : this(name, IdOptions.None) { + } + public IdentifierTerminal(string name, IdOptions options) : this(name, "_", "_") { + Options = options; + } + public IdentifierTerminal(string name, string extraChars, string extraFirstChars = ""): base(name) { + AllFirstChars = Strings.AllLatinLetters + extraFirstChars; + AllChars = Strings.AllLatinLetters + Strings.DecimalDigits + extraChars; + } + + public void AddPrefix(string prefix, IdOptions options) { + base.AddPrefixFlag(prefix, (short)options); + } + #endregion + + #region properties: AllChars, AllFirstChars + CharHashSet _allCharsSet; + CharHashSet _allFirstCharsSet; + + public string AllFirstChars; + public string AllChars; + public TokenEditorInfo KeywordEditorInfo = new TokenEditorInfo(TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None); + public IdOptions Options; //flags for the case when there are no prefixes + public CaseRestriction CaseRestriction; + + public readonly UnicodeCategoryList StartCharCategories = new UnicodeCategoryList(); //categories of first char + public readonly UnicodeCategoryList CharCategories = new UnicodeCategoryList(); //categories of all other chars + public readonly UnicodeCategoryList CharsToRemoveCategories = new UnicodeCategoryList(); //categories of chars to remove from final id, usually formatting category + #endregion + + #region overrides + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + _allCharsSet = new CharHashSet(Grammar.CaseSensitive); + _allCharsSet.UnionWith(AllChars.ToCharArray()); + + //Adjust case restriction. We adjust only first chars; if first char is ok, we will scan the rest without restriction + // and then check casing for entire identifier + switch(CaseRestriction) { + case CaseRestriction.AllLower: + case CaseRestriction.FirstLower: + _allFirstCharsSet = new CharHashSet(true); + _allFirstCharsSet.UnionWith(AllFirstChars.ToLowerInvariant().ToCharArray()); + break; + case CaseRestriction.AllUpper: + case CaseRestriction.FirstUpper: + _allFirstCharsSet = new CharHashSet(true); + _allFirstCharsSet.UnionWith(AllFirstChars.ToUpperInvariant().ToCharArray()); + break; + default: //None + _allFirstCharsSet = new CharHashSet(Grammar.CaseSensitive); + _allFirstCharsSet.UnionWith(AllFirstChars.ToCharArray()); + break; + } + //if there are "first" chars defined by categories, add the terminal to FallbackTerminals + if (this.StartCharCategories.Count > 0) + grammarData.NoPrefixTerminals.Add(this); + if (this.EditorInfo == null) + this.EditorInfo = new TokenEditorInfo(TokenType.Identifier, TokenColor.Identifier, TokenTriggers.None); + } + + //TODO: put into account non-Ascii aplhabets specified by means of Unicode categories! + public override IList GetFirsts() { + var list = new StringList(); + list.AddRange(Prefixes); + foreach (char ch in _allFirstCharsSet) + list.Add(ch.ToString()); + if ((Options & IdOptions.CanStartWithEscape) != 0) + list.Add(this.EscapeChar.ToString()); + return list; + } + + protected override void InitDetails(ParsingContext context, CompoundTokenDetails details) { + base.InitDetails(context, details); + details.Flags = (short)Options; + } + + //Override to assign IsKeyword flag to keyword tokens + protected override Token CreateToken(ParsingContext context, ISourceStream source, CompoundTokenDetails details) { + Token token = base.CreateToken(context, source, details); + if (details.IsSet((short)IdOptions.IsNotKeyword)) + return token; + //check if it is keyword + CheckReservedWord(token); + return token; + } + private void CheckReservedWord(Token token) { + KeyTerm keyTerm; + if (Grammar.KeyTerms.TryGetValue(token.Text, out keyTerm)) { + token.KeyTerm = keyTerm; + //if it is reserved word, then overwrite terminal + if (keyTerm.Flags.IsSet(TermFlags.IsReservedWord)) + token.SetTerminal(keyTerm); + } + } + + protected override Token QuickParse(ParsingContext context, ISourceStream source) { + if (!_allFirstCharsSet.Contains(source.PreviewChar)) + return null; + source.PreviewPosition++; + while (_allCharsSet.Contains(source.PreviewChar) && !source.EOF()) + source.PreviewPosition++; + //if it is not a terminator then cancel; we need to go through full algorithm + if (!this.Grammar.IsWhitespaceOrDelimiter(source.PreviewChar)) + return null; + var token = source.CreateToken(this.OutputTerminal); + if(CaseRestriction != CaseRestriction.None && !CheckCaseRestriction(token.ValueString)) + return null; + //!!! Do not convert to common case (all-lower) for case-insensitive grammar. Let identifiers remain as is, + // it is responsibility of interpreter to provide case-insensitive read/write operations for identifiers + // if (!this.GrammarData.Grammar.CaseSensitive) + // token.Value = token.Text.ToLower(CultureInfo.InvariantCulture); + CheckReservedWord(token); + return token; + } + + protected override bool ReadBody(ISourceStream source, CompoundTokenDetails details) { + int start = source.PreviewPosition; + bool allowEscapes = details.IsSet((short)IdOptions.AllowsEscapes); + CharList outputChars = new CharList(); + while (!source.EOF()) { + char current = source.PreviewChar; + if (Grammar.IsWhitespaceOrDelimiter(current)) + break; + if (allowEscapes && current == this.EscapeChar) { + current = ReadUnicodeEscape(source, details); + //We need to back off the position. ReadUnicodeEscape sets the position to symbol right after escape digits. + //This is the char that we should process in next iteration, so we must backup one char, to pretend the escaped + // char is at position of last digit of escape sequence. + source.PreviewPosition--; + if (details.Error != null) + return false; + } + //Check if current character is OK + if (!CharOk(current, source.PreviewPosition == start)) + break; + //Check if we need to skip this char + UnicodeCategory currCat = char.GetUnicodeCategory(current); //I know, it suxx, we do it twice, fix it later + if (!this.CharsToRemoveCategories.Contains(currCat)) + outputChars.Add(current); //add it to output (identifier) + source.PreviewPosition++; + }//while + if (outputChars.Count == 0) + return false; + //Convert collected chars to string + details.Body = new string(outputChars.ToArray()); + if (!CheckCaseRestriction(details.Body)) + return false; + return !string.IsNullOrEmpty(details.Body); + } + + private bool CharOk(char ch, bool first) { + //first check char lists, then categories + var charSet = first? _allFirstCharsSet : _allCharsSet; + if(charSet.Contains(ch)) return true; + //check categories + if (CharCategories.Count > 0) { + UnicodeCategory chCat = char.GetUnicodeCategory(ch); + UnicodeCategoryList catList = first ? StartCharCategories : CharCategories; + if (catList.Contains(chCat)) return true; + } + return false; + } + + private bool CheckCaseRestriction(string body) { + switch(CaseRestriction) { + case CaseRestriction.FirstLower: return Char.IsLower(body, 0); + case CaseRestriction.FirstUpper: return Char.IsUpper(body, 0); + case CaseRestriction.AllLower: return body.ToLower() == body; + case CaseRestriction.AllUpper: return body.ToUpper() == body; + default : return true; + } + }//method + + + private char ReadUnicodeEscape(ISourceStream source, CompoundTokenDetails details) { + //Position is currently at "\" symbol + source.PreviewPosition++; //move to U/u char + int len; + switch (source.PreviewChar) { + case 'u': len = 4; break; + case 'U': len = 8; break; + default: + details.Error = Resources.ErrInvEscSymbol; // "Invalid escape symbol, expected 'u' or 'U' only." + return '\0'; + } + if (source.PreviewPosition + len > source.Text.Length) { + details.Error = Resources.ErrInvEscSeq; // "Invalid escape sequence"; + return '\0'; + } + source.PreviewPosition++; //move to the first digit + string digits = source.Text.Substring(source.PreviewPosition, len); + char result = (char)Convert.ToUInt32(digits, 16); + source.PreviewPosition += len; + details.Flags |= (int) IdFlagsInternal.HasEscapes; + return result; + } + + protected override bool ConvertValue(CompoundTokenDetails details) { + if (details.IsSet((short)IdOptions.NameIncludesPrefix)) + details.Value = details.Prefix + details.Body; + else + details.Value = details.Body; + return true; + } + + #endregion + + }//class + + +} //namespace diff --git a/Irony/Parsing/Terminals/ImpliedSymbolTerminal.cs b/Irony/Parsing/Terminals/ImpliedSymbolTerminal.cs new file mode 100644 index 0000000..6f7d528 --- /dev/null +++ b/Irony/Parsing/Terminals/ImpliedSymbolTerminal.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + //In some grammars there is a situation when some operator symbol can be skipped in source text and should be implied by parser. + // In arithmetics, we often imply "*" operator in formulas: + // x y => x * y. + // The SearchGrammar in Samples provides another example: two consequtive terms imply "and" operator and should be treated as such: + // x y => x AND y + // We could use a simple nullable Non-terminal terminal in this case, but the problem is that we cannot associate precedence + // and associativity with non-terminal, only with terminals. Precedence is important here because the implied symbol identifies binary + // operation, so parser should be able to use precedence value(s) when resolving shift/reduce ambiguity. + // So here comes ImpliedSymbolTerminal - it is a terminal that produces a token with empty text. + // It relies on scanner-parser link enabled - so the implied symbol token is created ONLY + // when the current parser state allows it and there are no other alternatives (hence lowest priority value). + // See SearchGrammar as an example of use of this terminal. + public class ImpliedSymbolTerminal : Terminal { + public ImpliedSymbolTerminal(string name) : base(name) { + this.Priority = TerminalPriority.Low; //This terminal should be tried after all candidate terminals failed. + } + + public override void Init(Irony.Parsing.GrammarData grammarData) { + base.Init(grammarData); + //Check that Parser-scanner link is enabled - this terminal can be used only if this link is enabled + if (Grammar.LanguageFlags.IsSet(LanguageFlags.DisableScannerParserLink)) + grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrImpliedOpUseParserLink, this.Name); + //"ImpliedSymbolTerminal cannot be used in grammar with DisableScannerParserLink flag set" + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + return source.CreateToken(this); //Create an empty token representing an implied symbol. + } + + }//class +}//namespace diff --git a/Irony/Parsing/Terminals/KeyTerm.cs b/Irony/Parsing/Terminals/KeyTerm.cs new file mode 100644 index 0000000..06a4fdd --- /dev/null +++ b/Irony/Parsing/Terminals/KeyTerm.cs @@ -0,0 +1,114 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + public class KeyTermTable : Dictionary { + public KeyTermTable(StringComparer comparer) : base(100, comparer) {} + } + public class KeyTermList : List { } + + //Keyterm is a keyword or a special symbol used in grammar rules, for example: begin, end, while, =, *, etc. + // So "key" comes from the Keyword. + public class KeyTerm : Terminal { + public KeyTerm(string text, string name) : base(name) { + Text = text; + base.ErrorAlias = name; + this.Flags |= TermFlags.NoAstNode; + } + + public string Text {get; private set;} + + //Normally false, meaning keywords (symbols in grammar consisting of letters) cannot be followed by a letter or digit + public bool AllowAlphaAfterKeyword = false; + + #region overrides: TryMatch, Init, GetPrefixes(), ToString() + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + + #region comments about keyterms priority + // Priority - determines the order in which multiple terminals try to match input for a given current char in the input. + // For a given input char the scanner looks up the collection of terminals that may match this input symbol. It is the order + // in this collection that is determined by Priority value - the higher the priority, the earlier the terminal gets a chance + // to check the input. + // Keywords found in grammar by default have lowest priority to allow other terminals (like identifiers)to check the input first. + // Additionally, longer symbols have higher priority, so symbols like "+=" should have higher priority value than "+" symbol. + // As a result, Scanner would first try to match "+=", longer symbol, and if it fails, it will try "+". + // Reserved words are the opposite - they have the highest priority + #endregion + if (Flags.IsSet(TermFlags.IsReservedWord)) + base.Priority = TerminalPriority.ReservedWords + Text.Length; //the longer the word, the higher is the priority + else + base.Priority = TerminalPriority.Low + Text.Length; + //Setup editor info + if (this.EditorInfo != null) return; + TokenType tknType = TokenType.Identifier; + if (Flags.IsSet(TermFlags.IsOperator)) + tknType |= TokenType.Operator; + else if (Flags.IsSet(TermFlags.IsDelimiter | TermFlags.IsPunctuation)) + tknType |= TokenType.Delimiter; + TokenTriggers triggers = TokenTriggers.None; + if (this.Flags.IsSet(TermFlags.IsBrace)) + triggers |= TokenTriggers.MatchBraces; + if (this.Flags.IsSet(TermFlags.IsMemberSelect)) + triggers |= TokenTriggers.MemberSelect; + TokenColor color = TokenColor.Text; + if (Flags.IsSet(TermFlags.IsKeyword)) + color = TokenColor.Keyword; + this.EditorInfo = new TokenEditorInfo(tknType, color, triggers); + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + if (!source.MatchSymbol(Text)) + return null; + source.PreviewPosition += Text.Length; + //In case of keywords, check that it is not followed by letter or digit + if (this.Flags.IsSet(TermFlags.IsKeyword) && !AllowAlphaAfterKeyword) { + var previewChar = source.PreviewChar; + if (char.IsLetterOrDigit(previewChar) || previewChar == '_') return null; //reject + } + var token = source.CreateToken(this.OutputTerminal, Text); + return token; + } + + public override IList GetFirsts() { + return new string[] { Text }; + } + public override string ToString() { + if (Name != Text) return Name; + return Text; + } + public override string TokenToString(Token token) { + var keyw = Flags.IsSet(TermFlags.IsKeyword)? Resources.LabelKeyword : Resources.LabelKeySymbol ; //"(Keyword)" : "(Key symbol)" + var result = (token.ValueString ?? token.Text) + " " + keyw; + return result; + } + #endregion + + [System.Diagnostics.DebuggerStepThrough] + public override bool Equals(object obj) { + return base.Equals(obj); + } + + [System.Diagnostics.DebuggerStepThrough] + public override int GetHashCode() { + return Text.GetHashCode(); + } + + }//class + + +} diff --git a/Irony/Parsing/Terminals/LineContinuationTerminal.cs b/Irony/Parsing/Terminals/LineContinuationTerminal.cs new file mode 100644 index 0000000..979a1a5 --- /dev/null +++ b/Irony/Parsing/Terminals/LineContinuationTerminal.cs @@ -0,0 +1,117 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + public class LineContinuationTerminal : Terminal { + + public LineContinuationTerminal(string name, params string[] startSymbols) : base(name, TokenCategory.Outline) { + var symbols = startSymbols.Where(s => !IsNullOrWhiteSpace(s)).ToArray(); + StartSymbols = new StringList(symbols); + if (StartSymbols.Count == 0) + StartSymbols.AddRange(_defaultStartSymbols); + Priority = TerminalPriority.High; + } + + public StringList StartSymbols; + private string _startSymbolsFirsts = String.Concat(_defaultStartSymbols); + static string[] _defaultStartSymbols = new[] { "\\", "_" }; + public string LineTerminators = "\n\r\v"; + + #region overrides + + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + + // initialize string of start characters for fast lookup + _startSymbolsFirsts = new String(StartSymbols.Select(s => s.First()).ToArray()); + + if (this.EditorInfo == null) { + this.EditorInfo = new TokenEditorInfo(TokenType.Delimiter, TokenColor.Comment, TokenTriggers.None); + } + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + // Quick check + var lookAhead = source.PreviewChar; + var startIndex = _startSymbolsFirsts.IndexOf(lookAhead); + if (startIndex < 0) + return null; + + // Match start symbols + if (!BeginMatch(source, startIndex, lookAhead)) + return null; + + // Match NewLine + var result = CompleteMatch(source); + if (result != null) + return result; + + // Report an error + return context.CreateErrorToken(Resources.ErrNewLineExpected); + } + + private bool BeginMatch(ISourceStream source, int startFrom, char lookAhead) { + foreach (var startSymbol in StartSymbols.Skip(startFrom)) { + if (startSymbol[0] != lookAhead) + continue; + if (source.MatchSymbol(startSymbol)) { + source.PreviewPosition += startSymbol.Length; + return true; + } + } + return false; + } + + private Token CompleteMatch(ISourceStream source) { + if (source.EOF()) + return null; + + do { + // Match NewLine + var lookAhead = source.PreviewChar; + if (LineTerminators.IndexOf(lookAhead) >= 0) + { + source.PreviewPosition++; + // Treat \r\n as single NewLine + if (!source.EOF() && lookAhead == '\r' && source.PreviewChar == '\n') + source.PreviewPosition++; + break; + } + + // Eat up whitespace + if (this.Grammar.IsWhitespaceOrDelimiter(lookAhead)) + { + source.PreviewPosition++; + continue; + } + + // Fail on anything else + return null; + } + while (!source.EOF()); + + // Create output token + return source.CreateToken(this.OutputTerminal); + } + + public override IList GetFirsts() { + return StartSymbols; + } + + #endregion + + private static bool IsNullOrWhiteSpace(string s) { +#if VS2008 + if (String.IsNullOrEmpty(s)) + return true; + return s.Trim().Length == 0; +#else + return String.IsNullOrWhiteSpace(s); +#endif + } + + } // LineContinuationTerminal class +} diff --git a/Irony/Parsing/Terminals/NewLineTerminal.cs b/Irony/Parsing/Terminals/NewLineTerminal.cs new file mode 100644 index 0000000..14a7ea8 --- /dev/null +++ b/Irony/Parsing/Terminals/NewLineTerminal.cs @@ -0,0 +1,56 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + //This is a simple NewLine terminal recognizing line terminators for use in grammars for line-based languages like VB + // instead of more complex alternative of using CodeOutlineFilter. + public class NewLineTerminal : Terminal { + public NewLineTerminal(string name) : base(name, TokenCategory.Outline) { + base.ErrorAlias = Resources.LabelLineBreak; // "[line break]"; + this.Flags |= TermFlags.IsPunctuation; + } + + public string LineTerminators = "\n\r\v"; + + #region overrides: Init, GetFirsts, TryMatch + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + Grammar.UsesNewLine = true; //That will prevent SkipWhitespace method from skipping new-line chars + } + public override IList GetFirsts() { + StringList firsts = new StringList(); + foreach(char t in LineTerminators) + firsts.Add(t.ToString()); + return firsts; + } + public override Token TryMatch(ParsingContext context, ISourceStream source) { + char current = source.PreviewChar; + if (!LineTerminators.Contains(current)) return null; + //Treat \r\n as a single terminator + bool doExtraShift = (current == '\r' && source.NextPreviewChar == '\n'); + source.PreviewPosition++; //main shift + if (doExtraShift) + source.PreviewPosition++; + Token result = source.CreateToken(this.OutputTerminal); + return result; + } + + #endregion + + + }//class +}//namespace diff --git a/Irony/Parsing/Terminals/NumberLiteral.cs b/Irony/Parsing/Terminals/NumberLiteral.cs new file mode 100644 index 0000000..3dfa53d --- /dev/null +++ b/Irony/Parsing/Terminals/NumberLiteral.cs @@ -0,0 +1,507 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion +//Authors: Roman Ivantsov - initial implementation and some later edits +// Philipp Serr - implementation of advanced features for c#, python, VB + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Diagnostics; +using Irony.Ast; + +namespace Irony.Parsing { + using BigInteger = System.Numerics.BigInteger; //Microsoft.Scripting.Math.BigInteger; + using Complex64 = System.Numerics.Complex; + using Irony.Ast; // Microsoft.Scripting.Math.Complex64; + + [Flags] + public enum NumberOptions { + None = 0, + Default = None, + + AllowStartEndDot = 0x01, //python : http://docs.python.org/ref/floating.html + IntOnly = 0x02, + NoDotAfterInt = 0x04, //for use with IntOnly flag; essentially tells terminal to avoid matching integer if + // it is followed by dot (or exp symbol) - leave to another terminal that will handle float numbers + AllowSign = 0x08, + DisableQuickParse = 0x10, + AllowLetterAfter = 0x20, // allow number be followed by a letter or underscore; by default this flag is not set, so "3a" would not be + // recognized as number followed by an identifier + AllowUnderscore = 0x40, // Ruby allows underscore inside number: 1_234 + + //The following should be used with base-identifying prefixes + Binary = 0x0100, //e.g. GNU GCC C Extension supports binary number literals + Octal = 0x0200, + Hex = 0x0400, + } + + + public class NumberLiteral : CompoundTerminalBase { + + //Flags for internal use + public enum NumberFlagsInternal : short { + HasDot = 0x1000, + HasExp = 0x2000, + } + //nested helper class + public class ExponentsTable : Dictionary { } + + #region Public Consts + //currently using TypeCodes for identifying numeric types + public const TypeCode TypeCodeBigInt = (TypeCode)30; + public const TypeCode TypeCodeImaginary = (TypeCode)31; + #endregion + + #region constructors and initialization + public NumberLiteral(string name) : this(name, NumberOptions.Default) { + } + public NumberLiteral(string name, NumberOptions options, Type astNodeType) : this(name, options) { + base.AstConfig.NodeType = astNodeType; + } + public NumberLiteral(string name, NumberOptions options, AstNodeCreator astNodeCreator) : this(name, options) { + base.AstConfig.NodeCreator = astNodeCreator; + } + public NumberLiteral(string name, NumberOptions options) : base(name) { + Options = options; + base.SetFlag(TermFlags.IsLiteral); + } + public void AddPrefix(string prefix, NumberOptions options) { + PrefixFlags.Add(prefix, (short) options); + Prefixes.Add(prefix); + } + public void AddExponentSymbols(string symbols, TypeCode floatType) { + foreach(var exp in symbols) + _exponentsTable[exp] = floatType; + } + #endregion + + #region Public fields/properties: ExponentSymbols, Suffixes + public NumberOptions Options; + public char DecimalSeparator = '.'; + + //Default types are assigned to literals without suffixes; first matching type used + public TypeCode[] DefaultIntTypes = new TypeCode[] { TypeCode.Int32 }; + public TypeCode DefaultFloatType = TypeCode.Double; + private ExponentsTable _exponentsTable = new ExponentsTable(); + + public bool IsSet(NumberOptions option) { + return (Options & option) != 0; + } + #endregion + + #region Private fields: _quickParseTerminators + #endregion + + #region overrides + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + //Default Exponent symbols if table is empty + if(_exponentsTable.Count == 0 && !IsSet(NumberOptions.IntOnly)) { + _exponentsTable['e'] = DefaultFloatType; + _exponentsTable['E'] = DefaultFloatType; + } + if (this.EditorInfo == null) + this.EditorInfo = new TokenEditorInfo(TokenType.Literal, TokenColor.Number, TokenTriggers.None); + } + + public override IList GetFirsts() { + StringList result = new StringList(); + result.AddRange(base.Prefixes); + //we assume that prefix is always optional, so number can always start with plain digit + result.AddRange(new string[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }); + // Python float numbers can start with a dot + if (IsSet(NumberOptions.AllowStartEndDot)) + result.Add(DecimalSeparator.ToString()); + if (IsSet(NumberOptions.AllowSign)) + result.AddRange(new string[] {"-", "+"} ); + return result; + } + + //Most numbers in source programs are just one-digit instances of 0, 1, 2, and maybe others until 9 + // so we try to do a quick parse for these, without starting the whole general process + protected override Token QuickParse(ParsingContext context, ISourceStream source) { + if (IsSet(NumberOptions.DisableQuickParse)) return null; + char current = source.PreviewChar; + //it must be a digit followed by a whitespace or delimiter + if (!char.IsDigit(current)) return null; + if (!Grammar.IsWhitespaceOrDelimiter(source.NextPreviewChar)) + return null; + int iValue = current - '0'; + object value = null; + switch (DefaultIntTypes[0]) { + case TypeCode.Int32: value = iValue; break; + case TypeCode.UInt32: value = (UInt32)iValue; break; + case TypeCode.Byte: value = (byte)iValue; break; + case TypeCode.SByte: value = (sbyte) iValue; break; + case TypeCode.Int16: value = (Int16)iValue; break; + case TypeCode.UInt16: value = (UInt16)iValue; break; + default: return null; + } + source.PreviewPosition++; + return source.CreateToken(this.OutputTerminal, value); + } + + protected override void InitDetails(ParsingContext context, CompoundTokenDetails details) { + base.InitDetails(context, details); + details.Flags = (short) this.Options; + } + + protected override void ReadPrefix(ISourceStream source, CompoundTokenDetails details) { + //check that is not a 0 followed by dot; + //this may happen in Python for number "0.123" - we can mistakenly take "0" as octal prefix + if (source.PreviewChar == '0' && source.NextPreviewChar == '.') return; + base.ReadPrefix(source, details); + }//method + + protected override bool ReadBody(ISourceStream source, CompoundTokenDetails details) { + //remember start - it may be different from source.TokenStart, we may have skipped prefix + int start = source.PreviewPosition; + char current = source.PreviewChar; + if (IsSet(NumberOptions.AllowSign) && (current == '-' || current == '+')) { + details.Sign = current.ToString(); + source.PreviewPosition++; + } + //Figure out digits set + string digits = GetDigits(details); + bool isDecimal = !details.IsSet((short) (NumberOptions.Binary | NumberOptions.Octal | NumberOptions.Hex)); + bool allowFloat = !IsSet(NumberOptions.IntOnly); + bool foundDigits = false; + + while (!source.EOF()) { + current = source.PreviewChar; + //1. If it is a digit, just continue going; the same for '_' if it is allowed + if (digits.IndexOf(current) >= 0 || IsSet(NumberOptions.AllowUnderscore) && current == '_') { + source.PreviewPosition++; + foundDigits = true; + continue; + } + //2. Check if it is a dot in float number + bool isDot = current == DecimalSeparator; + if (allowFloat && isDot) { + //If we had seen already a dot or exponent, don't accept this one; + bool hasDotOrExp = details.IsSet((short) (NumberFlagsInternal.HasDot | NumberFlagsInternal.HasExp)); + if (hasDotOrExp) break; //from while loop + //In python number literals (NumberAllowPointFloat) a point can be the first and last character, + //We accept dot only if it is followed by a digit + if (digits.IndexOf(source.NextPreviewChar) < 0 && !IsSet(NumberOptions.AllowStartEndDot)) + break; //from while loop + details.Flags |= (int) NumberFlagsInternal.HasDot; + source.PreviewPosition++; + continue; + } + //3. Check if it is int number followed by dot or exp symbol + bool isExpSymbol = (details.ExponentSymbol == null) && _exponentsTable.ContainsKey(current); + if (!allowFloat && foundDigits && (isDot || isExpSymbol)) { + //If no partial float allowed then return false - it is not integer, let float terminal recognize it as float + if (IsSet(NumberOptions.NoDotAfterInt)) return false; + //otherwise break, it is integer and we're done reading digits + break; + } + + + //4. Only for decimals - check if it is (the first) exponent symbol + if (allowFloat && isDecimal && isExpSymbol) { + char next = source.NextPreviewChar; + bool nextIsSign = next == '-' || next == '+'; + bool nextIsDigit = digits.IndexOf(next) >= 0; + if (!nextIsSign && !nextIsDigit) + break; //Exponent should be followed by either sign or digit + //ok, we've got real exponent + details.ExponentSymbol = current.ToString(); //remember the exp char + details.Flags |= (int) NumberFlagsInternal.HasExp; + source.PreviewPosition++; + if (nextIsSign) + source.PreviewPosition++; //skip +/- explicitly so we don't have to deal with them on the next iteration + continue; + } + //4. It is something else (not digit, not dot or exponent) - we're done + break; //from while loop + }//while + int end = source.PreviewPosition; + if (!foundDigits) + return false; + details.Body = source.Text.Substring(start, end - start); + return true; + } + + protected internal override void OnValidateToken(ParsingContext context) { + if (!IsSet(NumberOptions.AllowLetterAfter)) { + var current = context.Source.PreviewChar; + if(char.IsLetter(current) || current == '_') { + context.CurrentToken = context.CreateErrorToken(Resources.ErrNoLetterAfterNum); // "Number cannot be followed by a letter." + } + } + base.OnValidateToken(context); + } + + protected override bool ConvertValue(CompoundTokenDetails details) { + if (String.IsNullOrEmpty(details.Body)) { + details.Error = Resources.ErrInvNumber; // "Invalid number."; + return false; + } + AssignTypeCodes(details); + //check for underscore + if (IsSet(NumberOptions.AllowUnderscore) && details.Body.Contains("_")) + details.Body = details.Body.Replace("_", string.Empty); + + //Try quick paths + switch (details.TypeCodes[0]) { + case TypeCode.Int32: + if (QuickConvertToInt32(details)) return true; + break; + case TypeCode.Double: + if (QuickConvertToDouble(details)) return true; + break; + } + + //Go full cycle + details.Value = null; + foreach (TypeCode typeCode in details.TypeCodes) { + switch (typeCode) { + case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: case TypeCodeImaginary: + return ConvertToFloat(typeCode, details); + case TypeCode.SByte: case TypeCode.Byte: case TypeCode.Int16: case TypeCode.UInt16: + case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: + if (details.Value == null) //if it is not done yet + TryConvertToLong(details, typeCode == TypeCode.UInt64); //try to convert to Long/Ulong and place the result into details.Value field; + if(TryCastToIntegerType(typeCode, details)) //now try to cast the ULong value to the target type + return true; + break; + case TypeCodeBigInt: + if (ConvertToBigInteger(details)) return true; + break; + }//switch + } + return false; + }//method + + private void AssignTypeCodes(CompoundTokenDetails details) { + //Type could be assigned when we read suffix; if so, just exit + if (details.TypeCodes != null) return; + //Decide on float types + var hasDot = details.IsSet((short)(NumberFlagsInternal.HasDot)); + var hasExp = details.IsSet((short)(NumberFlagsInternal.HasExp)); + var isFloat = (hasDot || hasExp); + if (!isFloat) { + details.TypeCodes = DefaultIntTypes; + return; + } + //so we have a float. If we have exponent symbol then use it to select type + if (hasExp) { + TypeCode code; + if (_exponentsTable.TryGetValue(details.ExponentSymbol[0], out code)) { + details.TypeCodes = new TypeCode[] {code}; + return; + } + }//if hasExp + //Finally assign default float type + details.TypeCodes = new TypeCode[] {DefaultFloatType}; + } + + #endregion + + #region private utilities + private bool QuickConvertToInt32(CompoundTokenDetails details) { + int radix = GetRadix(details); + if (radix == 10 && details.Body.Length > 10) return false; //10 digits is maximum for int32; int32.MaxValue = 2 147 483 647 + try { + //workaround for .Net FX bug: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=278448 + int iValue = 0; + if (radix == 10) + iValue = Convert.ToInt32(details.Body, CultureInfo.InvariantCulture); + else + iValue = Convert.ToInt32(details.Body, radix); + details.Value = iValue; + return true; + } catch { + return false; + } + }//method + + private bool QuickConvertToDouble(CompoundTokenDetails details) { + if (details.IsSet((short)(NumberOptions.Binary | NumberOptions.Octal | NumberOptions.Hex))) return false; + if (details.IsSet((short)(NumberFlagsInternal.HasExp))) return false; + if (DecimalSeparator != '.') return false; + double dvalue; + if (!double.TryParse(details.Body, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out dvalue)) return false; + details.Value = dvalue; + return true; + } + + + private bool ConvertToFloat(TypeCode typeCode, CompoundTokenDetails details) { + //only decimal numbers can be fractions + if (details.IsSet((short)(NumberOptions.Binary | NumberOptions.Octal | NumberOptions.Hex))) { + details.Error = Resources.ErrInvNumber; // "Invalid number."; + return false; + } + string body = details.Body; + //Some languages allow exp symbols other than E. Check if it is the case, and change it to E + // - otherwise .NET conversion methods may fail + if (details.IsSet((short)NumberFlagsInternal.HasExp) && details.ExponentSymbol.ToUpper() != "E") + body = body.Replace(details.ExponentSymbol, "E"); + + //'.' decimal seperator required by invariant culture + if (details.IsSet((short)NumberFlagsInternal.HasDot) && DecimalSeparator != '.') + body = body.Replace(DecimalSeparator, '.'); + + switch (typeCode) { + case TypeCode.Double: + case TypeCodeImaginary: + double dValue; + if (!Double.TryParse(body, NumberStyles.Float, CultureInfo.InvariantCulture, out dValue)) return false; + if (typeCode == TypeCodeImaginary) + details.Value = new Complex64(0, dValue); + else + details.Value = dValue; + return true; + case TypeCode.Single: + float fValue; + if (!Single.TryParse(body, NumberStyles.Float, CultureInfo.InvariantCulture, out fValue)) return false; + details.Value = fValue; + return true; + case TypeCode.Decimal: + decimal decValue; + if (!Decimal.TryParse(body, NumberStyles.Float, CultureInfo.InvariantCulture, out decValue)) return false; + details.Value = decValue; + return true; + }//switch + return false; + } + private bool TryCastToIntegerType(TypeCode typeCode, CompoundTokenDetails details) { + if (details.Value == null) return false; + try { + if (typeCode != TypeCode.UInt64) + details.Value = Convert.ChangeType(details.Value, typeCode, CultureInfo.InvariantCulture); + return true; + } catch (Exception) { + details.Error = string.Format(Resources.ErrCannotConvertValueToType, details.Value, typeCode.ToString()); + return false; + } + }//method + + private bool TryConvertToLong(CompoundTokenDetails details, bool useULong) { + try { + int radix = GetRadix(details); + //workaround for .Net FX bug: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=278448 + if (radix == 10) + if (useULong) + details.Value = Convert.ToUInt64(details.Body, CultureInfo.InvariantCulture); + else + details.Value = Convert.ToInt64(details.Body, CultureInfo.InvariantCulture); + else + if (useULong) + details.Value = Convert.ToUInt64(details.Body, radix); + else + details.Value = Convert.ToInt64(details.Body, radix); + return true; + } catch(OverflowException) { + details.Error = string.Format(Resources.ErrCannotConvertValueToType, details.Value, TypeCode.Int64.ToString()); + return false; + } + } + + private bool ConvertToBigInteger(CompoundTokenDetails details) { + //ignore leading zeros and sign + details.Body = details.Body.TrimStart('+').TrimStart('-').TrimStart('0'); + if (string.IsNullOrEmpty(details.Body)) + details.Body = "0"; + int bodyLength = details.Body.Length; + int radix = GetRadix(details); + int wordLength = GetSafeWordLength(details); + int sectionCount = GetSectionCount(bodyLength, wordLength); + ulong[] numberSections = new ulong[sectionCount]; //big endian + + try { + int startIndex = details.Body.Length - wordLength; + for (int sectionIndex = sectionCount - 1; sectionIndex >= 0; sectionIndex--) { + if (startIndex < 0) { + wordLength += startIndex; + startIndex = 0; + } + //workaround for .Net FX bug: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=278448 + if (radix == 10) + numberSections[sectionIndex] = Convert.ToUInt64(details.Body.Substring(startIndex, wordLength)); + else + numberSections[sectionIndex] = Convert.ToUInt64(details.Body.Substring(startIndex, wordLength), radix); + + startIndex -= wordLength; + } + } catch { + details.Error = Resources.ErrInvNumber;// "Invalid number."; + return false; + } + //produce big integer + ulong safeWordRadix = GetSafeWordRadix(details); + BigInteger bigIntegerValue = numberSections[0]; + for (int i = 1; i < sectionCount; i++) + bigIntegerValue = checked(bigIntegerValue * safeWordRadix + numberSections[i]); + if (details.Sign == "-") + bigIntegerValue = -bigIntegerValue; + details.Value = bigIntegerValue; + return true; + } + + private int GetRadix(CompoundTokenDetails details) { + if (details.IsSet((short)NumberOptions.Hex)) + return 16; + if (details.IsSet((short)NumberOptions.Octal)) + return 8; + if (details.IsSet((short)NumberOptions.Binary)) + return 2; + return 10; + } + private string GetDigits(CompoundTokenDetails details) { + if (details.IsSet((short)NumberOptions.Hex)) + return Strings.HexDigits; + if (details.IsSet((short)NumberOptions.Octal)) + return Strings.OctalDigits; + if (details.IsSet((short)NumberOptions.Binary)) + return Strings.BinaryDigits; + return Strings.DecimalDigits; + } + private int GetSafeWordLength(CompoundTokenDetails details) { + if (details.IsSet((short)NumberOptions.Hex)) + return 15; + if (details.IsSet((short)NumberOptions.Octal)) + return 21; //maxWordLength 22 + if (details.IsSet((short)NumberOptions.Binary)) + return 63; + return 19; //maxWordLength 20 + } + private int GetSectionCount(int stringLength, int safeWordLength) { + int quotient = stringLength / safeWordLength; + int remainder = stringLength - quotient * safeWordLength; + return remainder == 0 ? quotient : quotient + 1; + } + + //radix^safeWordLength + private ulong GetSafeWordRadix(CompoundTokenDetails details) { + if (details.IsSet((short)NumberOptions.Hex)) + return 1152921504606846976; + if (details.IsSet((short)NumberOptions.Octal)) + return 9223372036854775808; + if (details.IsSet((short) NumberOptions.Binary)) + return 9223372036854775808; + return 10000000000000000000; + } + private static bool IsIntegerCode(TypeCode code) { + return (code >= TypeCode.SByte && code <= TypeCode.UInt64); + } + #endregion + + + }//class + + +} diff --git a/Irony/Parsing/Terminals/QuotedValueLiteral.cs b/Irony/Parsing/Terminals/QuotedValueLiteral.cs new file mode 100644 index 0000000..eb54872 --- /dev/null +++ b/Irony/Parsing/Terminals/QuotedValueLiteral.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + //Terminal for reading values enclosed in a pair of start/end characters. For ex, date literal #15/10/2009# in VB + public class QuotedValueLiteral : DataLiteralBase { + public string StartSymbol; + public string EndSymbol; + + public QuotedValueLiteral(string name, string startEndSymbol, TypeCode dataType) : this(name, startEndSymbol, startEndSymbol, dataType) {} + + public QuotedValueLiteral(string name, string startSymbol, string endSymbol, TypeCode dataType) : base(name, dataType) { + StartSymbol = startSymbol; + EndSymbol = endSymbol; + } + + public override IList GetFirsts() { + return new string[] {StartSymbol}; + } + protected override string ReadBody(ParsingContext context, ISourceStream source) { + if (!source.MatchSymbol(StartSymbol)) return null; //this will result in null returned from TryMatch, no token + var start = source.Location.Position + StartSymbol.Length; + var end = source.Text.IndexOf(EndSymbol, start); + if (end < 0) return null; + var body = source.Text.Substring(start, end - start); + source.PreviewPosition = end + EndSymbol.Length; //move beyond the end of EndSymbol + return body; + } + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/RegExBasedTerminal.cs b/Irony/Parsing/Terminals/RegExBasedTerminal.cs new file mode 100644 index 0000000..3fbf975 --- /dev/null +++ b/Irony/Parsing/Terminals/RegExBasedTerminal.cs @@ -0,0 +1,72 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Irony.Parsing { + + //Note: this class was not tested at all + // Based on contributions by CodePlex user sakana280 + // 12.09.2008 - breaking change! added "name" parameter to the constructor + public class RegexBasedTerminal : Terminal { + public RegexBasedTerminal(string pattern, params string[] prefixes) + : base("name") { + Pattern = pattern; + if (prefixes != null) + Prefixes.AddRange(prefixes); + } + public RegexBasedTerminal(string name, string pattern, params string[] prefixes) : base(name) { + Pattern = pattern; + if (prefixes != null) + Prefixes.AddRange(prefixes); + } + + #region public properties + public readonly string Pattern; + public readonly StringList Prefixes = new StringList(); + + public Regex Expression { + get { return _expression; } + } Regex _expression; + #endregion + + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + string workPattern = @"\G(" + Pattern + ")"; + RegexOptions options = (Grammar.CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase); + _expression = new Regex(workPattern, options); + if (this.EditorInfo == null) + this.EditorInfo = new TokenEditorInfo(TokenType.Unknown, TokenColor.Text, TokenTriggers.None); + } + + public override IList GetFirsts() { + return Prefixes; + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + Match m = _expression.Match(source.Text, source.PreviewPosition); + if (!m.Success || m.Index != source.PreviewPosition) + return null; + source.PreviewPosition += m.Length; + return source.CreateToken(this.OutputTerminal); + } + + }//class + + + + +}//namespace diff --git a/Irony/Parsing/Terminals/RegExLiteral.cs b/Irony/Parsing/Terminals/RegExLiteral.cs new file mode 100644 index 0000000..9abd440 --- /dev/null +++ b/Irony/Parsing/Terminals/RegExLiteral.cs @@ -0,0 +1,142 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Irony.Parsing { + // Regular expression literal, like javascript literal: /abc?/i + // Allows optional switches + // example: + // regex = /abc\\\/de/ + // matches fragments like "abc\/de" + // Note: switches are returned in token.Details field. Unlike in StringLiteral, we don't need to unescape the escaped chars, + // (this is the job of regex engine), we only need to correctly recognize the end of expression + + [Flags] + public enum RegexTermOptions { + None = 0, + AllowLetterAfter = 0x01, //if not set (default) then any following letter (after legal switches) is reported as invalid switch + CreateRegExObject = 0x02, //if set, token.Value contains Regex object; otherwise, it contains a pattern (string) + UniqueSwitches = 0x04, //require unique switches + + Default = CreateRegExObject | UniqueSwitches, + } + + public class RegexLiteral : Terminal { + public class RegexSwitchTable : Dictionary { } + + public Char StartSymbol = '/'; + public Char EndSymbol='/'; + public Char EscapeSymbol='\\'; + public RegexSwitchTable Switches = new RegexSwitchTable(); + public RegexOptions DefaultOptions = RegexOptions.None; + public RegexTermOptions Options = RegexTermOptions.Default; + + private char[] _stopChars; + + public RegexLiteral(string name) : base(name) { + Switches.Add('i', RegexOptions.IgnoreCase); + Switches.Add('g', RegexOptions.None); //not sure what to do with this flag? anybody, any advice? + Switches.Add('m', RegexOptions.Multiline); + base.SetFlag(TermFlags.IsLiteral); + } + + public RegexLiteral(string name, char startEndSymbol, char escapeSymbol) : base(name) { + StartSymbol = startEndSymbol; + EndSymbol = startEndSymbol; + EscapeSymbol = escapeSymbol; + }//constructor + + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + _stopChars = new char[] { EndSymbol, '\r', '\n' }; + } + public override IList GetFirsts() { + var result = new StringList(); + result.Add(StartSymbol.ToString()); + return result; + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + while (true) { + //Find next position + var newPos = source.Text.IndexOfAny(_stopChars, source.PreviewPosition + 1); + //we either didn't find it + if (newPos == -1) + return context.CreateErrorToken(Resources.ErrNoEndForRegex);// "No end symbol for regex literal." + source.PreviewPosition = newPos; + if (source.PreviewChar != EndSymbol) + //we hit CR or LF, this is an error + return context.CreateErrorToken(Resources.ErrNoEndForRegex); + if (!CheckEscaped(source)) + break; + } + source.PreviewPosition++; //move after end symbol + //save pattern length, we will need it + var patternLen = source.PreviewPosition - source.Location.Position - 2; //exclude start and end symbol + //read switches and turn them into options + RegexOptions options = RegexOptions.None; + var switches = string.Empty; + while(ReadSwitch(source, ref options)) { + if (IsSet(RegexTermOptions.UniqueSwitches) && switches.Contains(source.PreviewChar)) + return context.CreateErrorToken(Resources.ErrDupRegexSwitch, source.PreviewChar); // "Duplicate switch '{0}' for regular expression" + switches += source.PreviewChar.ToString(); + source.PreviewPosition++; + } + //check following symbol + if (!IsSet(RegexTermOptions.AllowLetterAfter)) { + var currChar = source.PreviewChar; + if (char.IsLetter(currChar) || currChar == '_') + return context.CreateErrorToken(Resources.ErrInvRegexSwitch, currChar); // "Invalid switch '{0}' for regular expression" + } + var token = source.CreateToken(this.OutputTerminal); + //we have token, now what's left is to set its Value field. It is either pattern itself, or Regex instance + string pattern = token.Text.Substring(1, patternLen); //exclude start and end symbol + object value = pattern; + if (IsSet(RegexTermOptions.CreateRegExObject)) { + value = new Regex(pattern, options); + } + token.Value = value; + token.Details = switches; //save switches in token.Details + return token; + } + + private bool CheckEscaped(ISourceStream source) { + var savePos = source.PreviewPosition; + bool escaped = false; + source.PreviewPosition--; + while (source.PreviewChar == EscapeSymbol){ + escaped = !escaped; + source.PreviewPosition--; + } + source.PreviewPosition = savePos; + return escaped; + } + private bool ReadSwitch(ISourceStream source, ref RegexOptions options) { + RegexOptions option; + var result = Switches.TryGetValue(source.PreviewChar, out option); + if (result) + options |= option; + return result; + } + + public bool IsSet(RegexTermOptions option) { + return (Options & option) != 0; + } + + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/StringLiteral.cs b/Irony/Parsing/Terminals/StringLiteral.cs new file mode 100644 index 0000000..4630bef --- /dev/null +++ b/Irony/Parsing/Terminals/StringLiteral.cs @@ -0,0 +1,401 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using Irony.Ast; + +namespace Irony.Parsing { + + [Flags] + public enum StringOptions : short { + None = 0, + IsChar = 0x01, + AllowsDoubledQuote = 0x02, //Convert doubled start/end symbol to a single symbol; for ex. in SQL, '' -> ' + AllowsLineBreak = 0x04, + IsTemplate = 0x08, //Can include embedded expressions that should be evaluated on the fly; ex in Ruby: "hello #{name}" + NoEscapes = 0x10, + AllowsUEscapes = 0x20, + AllowsXEscapes = 0x40, + AllowsOctalEscapes = 0x80, + AllowsAllEscapes = AllowsUEscapes | AllowsXEscapes | AllowsOctalEscapes, + + } + + //Container for settings of tempate string parser, to interpet strings having embedded values or expressions + // like in Ruby: + // "Hello, #{name}" + // Default values match settings for Ruby strings + public class StringTemplateSettings { + public string StartTag = "#{"; + public string EndTag = "}"; + public NonTerminal ExpressionRoot; + } + + public class StringLiteral : CompoundTerminalBase { + + public enum StringFlagsInternal : short { + HasEscapes = 0x100, + } + + #region StringSubType + class StringSubType { + internal readonly string Start, End; + internal readonly StringOptions Flags; + internal readonly byte Index; + internal StringSubType(string start, string end, StringOptions flags, byte index) { + Start = start; + End = end; + Flags = flags; + Index = index; + } + + internal static int LongerStartFirst(StringSubType x, StringSubType y) { + try {//in case any of them is null + if (x.Start.Length > y.Start.Length) return -1; + } catch { } + return 0; + } + } + class StringSubTypeList : List { + internal void Add(string start, string end, StringOptions flags) { + base.Add(new StringSubType(start, end, flags, (byte) this.Count)); + } + } + #endregion + + #region constructors and initialization + public StringLiteral(string name): base(name) { + base.SetFlag(TermFlags.IsLiteral); + } + + public StringLiteral(string name, string startEndSymbol, StringOptions options) : this(name) { + _subtypes.Add(startEndSymbol, startEndSymbol, options); + } + + public StringLiteral(string name, string startEndSymbol) : this(name, startEndSymbol, StringOptions.None) { } + + public StringLiteral(string name, string startEndSymbol, StringOptions options, Type astNodeType) + : this(name, startEndSymbol, options) { + base.AstConfig.NodeType = astNodeType; + } + public StringLiteral(string name, string startEndSymbol, StringOptions options, AstNodeCreator astNodeCreator) + : this(name, startEndSymbol, options) { + base.AstConfig.NodeCreator = astNodeCreator; + } + + public void AddStartEnd(string startEndSymbol, StringOptions stringOptions) { + AddStartEnd(startEndSymbol, startEndSymbol, stringOptions); + } + public void AddStartEnd(string startSymbol, string endSymbol, StringOptions stringOptions) { + _subtypes.Add(startSymbol, endSymbol, stringOptions); + } + public void AddPrefix(string prefix, StringOptions flags) { + base.AddPrefixFlag(prefix, (short)flags); + } + + #endregion + + #region Properties/Fields + private readonly StringSubTypeList _subtypes = new StringSubTypeList(); + string _startSymbolsFirsts; //first chars of start-end symbols + #endregion + + #region overrides: Init, GetFirsts, ReadBody, etc... + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + _startSymbolsFirsts = string.Empty; + if (_subtypes.Count == 0) { + grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrInvStrDef, this.Name); //"Error in string literal [{0}]: No start/end symbols specified." + return; + } + //collect all start-end symbols in lists and create strings of first chars + var allStartSymbols = new StringSet(); //to detect duplicate start symbols + _subtypes.Sort(StringSubType.LongerStartFirst); + bool isTemplate = false; + foreach (StringSubType subType in _subtypes) { + if (allStartSymbols.Contains(subType.Start)) + grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, + Resources.ErrDupStartSymbolStr, subType.Start, this.Name); //"Duplicate start symbol {0} in string literal [{1}]." + allStartSymbols.Add(subType.Start); + _startSymbolsFirsts += subType.Start[0].ToString(); + if ((subType.Flags & StringOptions.IsTemplate) != 0) isTemplate = true; + } + if (!CaseSensitivePrefixesSuffixes) + _startSymbolsFirsts = _startSymbolsFirsts.ToLower() + _startSymbolsFirsts.ToUpper(); + //Set multiline flag + foreach (StringSubType info in _subtypes) { + if ((info.Flags & StringOptions.AllowsLineBreak) != 0) { + SetFlag(TermFlags.IsMultiline); + break; + } + } + //For templates only + if(isTemplate) { + //Check that template settings object is provided + var templateSettings = this.AstConfig.Data as StringTemplateSettings; + if(templateSettings == null) + grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplNoSettings, this.Name); //"Error in string literal [{0}]: IsTemplate flag is set, but TemplateSettings is not provided." + else if (templateSettings.ExpressionRoot == null) + grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplMissingExprRoot, this.Name); //"" + else if(!Grammar.SnippetRoots.Contains(templateSettings.ExpressionRoot)) + grammarData.Language.Errors.Add(GrammarErrorLevel.Error, null, Resources.ErrTemplExprNotRoot, this.Name); //"" + }//if + //Create editor info + if (this.EditorInfo == null) + this.EditorInfo = new TokenEditorInfo(TokenType.String, TokenColor.String, TokenTriggers.None); + }//method + + public override IList GetFirsts() { + StringList result = new StringList(); + result.AddRange(Prefixes); + //we assume that prefix is always optional, so string can start with start-end symbol + foreach (char ch in _startSymbolsFirsts) + result.Add(ch.ToString()); + return result; + } + + protected override bool ReadBody(ISourceStream source, CompoundTokenDetails details) { + if (!details.PartialContinues) { + if (!ReadStartSymbol(source, details)) return false; + } + return CompleteReadBody(source, details); + } + + private bool CompleteReadBody(ISourceStream source, CompoundTokenDetails details) { + bool escapeEnabled = !details.IsSet((short) StringOptions.NoEscapes); + int start = source.PreviewPosition; + string endQuoteSymbol = details.EndSymbol; + string endQuoteDoubled = endQuoteSymbol + endQuoteSymbol; //doubled quote symbol + bool lineBreakAllowed = details.IsSet((short) StringOptions.AllowsLineBreak); + //1. Find the string end + // first get the position of the next line break; we are interested in it to detect malformed string, + // therefore do it only if linebreak is NOT allowed; if linebreak is allowed, set it to -1 (we don't care). + int nlPos = lineBreakAllowed ? -1 : source.Text.IndexOf('\n', source.PreviewPosition); + //fix by ashmind for EOF right after opening symbol + while (true) { + int endPos = source.Text.IndexOf(endQuoteSymbol, source.PreviewPosition); + //Check for partial token in line-scanning mode + if (endPos < 0 && details.PartialOk && lineBreakAllowed) { + ProcessPartialBody(source, details); + return true; + } + //Check for malformed string: either EndSymbol not found, or LineBreak is found before EndSymbol + bool malformed = endPos < 0 || nlPos >= 0 && nlPos < endPos; + if (malformed) { + //Set source position for recovery: move to the next line if linebreak is not allowed. + if (nlPos > 0) endPos = nlPos; + if (endPos > 0) source.PreviewPosition = endPos + 1; + details.Error = Resources.ErrBadStrLiteral;// "Mal-formed string literal - cannot find termination symbol."; + return true; //we did find start symbol, so it is definitely string, only malformed + }//if malformed + + if (source.EOF()) + return true; + + //We found EndSymbol - check if it is escaped; if yes, skip it and continue search + if (escapeEnabled && IsEndQuoteEscaped(source.Text, endPos)) { + source.PreviewPosition = endPos + endQuoteSymbol.Length; + continue; //searching for end symbol + } + + //Check if it is doubled end symbol + source.PreviewPosition = endPos; + if (details.IsSet((short)StringOptions.AllowsDoubledQuote) && source.MatchSymbol(endQuoteDoubled)) { + source.PreviewPosition = endPos + endQuoteDoubled.Length; + continue; + }//checking for doubled end symbol + + //Ok, this is normal endSymbol that terminates the string. + // Advance source position and get out from the loop + details.Body = source.Text.Substring(start, endPos - start); + source.PreviewPosition = endPos + endQuoteSymbol.Length; + return true; //if we come here it means we're done - we found string end. + } //end of loop to find string end; + } + private void ProcessPartialBody(ISourceStream source, CompoundTokenDetails details) { + int from = source.PreviewPosition; + source.PreviewPosition = source.Text.Length; + details.Body = source.Text.Substring(from, source.PreviewPosition - from); + details.IsPartial = true; + } + + protected override void InitDetails(ParsingContext context, CompoundTerminalBase.CompoundTokenDetails details) { + base.InitDetails(context, details); + if (context.VsLineScanState.Value != 0) { + //we are continuing partial string on the next line + details.Flags = context.VsLineScanState.TerminalFlags; + details.SubTypeIndex = context.VsLineScanState.TokenSubType; + var stringInfo = _subtypes[context.VsLineScanState.TokenSubType]; + details.StartSymbol = stringInfo.Start; + details.EndSymbol = stringInfo.End; + } + } + + protected override void ReadSuffix(ISourceStream source, CompoundTerminalBase.CompoundTokenDetails details) { + base.ReadSuffix(source, details); + //"char" type can be identified by suffix (like VB where c suffix identifies char) + // in this case we have details.TypeCodes[0] == char and we need to set the IsChar flag + if (details.TypeCodes != null && details.TypeCodes[0] == TypeCode.Char) + details.Flags |= (int)StringOptions.IsChar; + else + //we may have IsChar flag set (from startEndSymbol, like in c# single quote identifies char) + // in this case set type code + if (details.IsSet((short) StringOptions.IsChar)) + details.TypeCodes = new TypeCode[] { TypeCode.Char }; + } + + private bool IsEndQuoteEscaped(string text, int quotePosition) { + bool escaped = false; + int p = quotePosition - 1; + while (p > 0 && text[p] == EscapeChar) { + escaped = !escaped; + p--; + } + return escaped; + } + + private bool ReadStartSymbol(ISourceStream source, CompoundTokenDetails details) { + if (_startSymbolsFirsts.IndexOf(source.PreviewChar) < 0) + return false; + foreach (StringSubType subType in _subtypes) { + if (!source.MatchSymbol(subType.Start)) + continue; + //We found start symbol + details.StartSymbol = subType.Start; + details.EndSymbol = subType.End; + details.Flags |= (short) subType.Flags; + details.SubTypeIndex = subType.Index; + source.PreviewPosition += subType.Start.Length; + return true; + }//foreach + return false; + }//method + + + //Extract the string content from lexeme, adjusts the escaped and double-end symbols + protected override bool ConvertValue(CompoundTokenDetails details) { + string value = details.Body; + bool escapeEnabled = !details.IsSet((short)StringOptions.NoEscapes); + //Fix all escapes + if (escapeEnabled && value.IndexOf(EscapeChar) >= 0) { + details.Flags |= (int) StringFlagsInternal.HasEscapes; + string[] arr = value.Split(EscapeChar); + bool ignoreNext = false; + //we skip the 0 element as it is not preceeded by "\" + for (int i = 1; i < arr.Length; i++) { + if (ignoreNext) { + ignoreNext = false; + continue; + } + string s = arr[i]; + if (string.IsNullOrEmpty(s)) { + //it is "\\" - escaped escape symbol. + arr[i] = @"\"; + ignoreNext = true; + continue; + } + //The char is being escaped is the first one; replace it with char in Escapes table + char first = s[0]; + char newFirst; + if (Escapes.TryGetValue(first, out newFirst)) + arr[i] = newFirst + s.Substring(1); + else { + arr[i] = HandleSpecialEscape(arr[i], details); + }//else + }//for i + value = string.Join(string.Empty, arr); + }// if EscapeEnabled + + //Check for doubled end symbol + string endSymbol = details.EndSymbol; + if (details.IsSet((short)StringOptions.AllowsDoubledQuote) && value.IndexOf(endSymbol) >= 0) + value = value.Replace(endSymbol + endSymbol, endSymbol); + + if (details.IsSet((short)StringOptions.IsChar)) { + if (value.Length != 1) { + details.Error = Resources.ErrBadChar; //"Invalid length of char literal - should be a single character."; + return false; + } + details.Value = value[0]; + } else { + details.TypeCodes = new TypeCode[] { TypeCode.String }; + details.Value = value; + } + return true; + } + + //Should support: \Udddddddd, \udddd, \xdddd, \N{name}, \0, \ddd (octal), + protected virtual string HandleSpecialEscape(string segment, CompoundTokenDetails details) { + if (string.IsNullOrEmpty(segment)) return string.Empty; + int len, p; string digits; char ch; string result; + char first = segment[0]; + switch (first) { + case 'u': + case 'U': + if (details.IsSet((short)StringOptions.AllowsUEscapes)) { + len = (first == 'u' ? 4 : 8); + if (segment.Length < len + 1) { + details.Error = string.Format(Resources.ErrBadUnEscape, segment.Substring(len + 1), len);// "Invalid unicode escape ({0}), expected {1} hex digits." + return segment; + } + digits = segment.Substring(1, len); + ch = (char) Convert.ToUInt32(digits, 16); + result = ch + segment.Substring(len + 1); + return result; + }//if + break; + case 'x': + if (details.IsSet((short)StringOptions.AllowsXEscapes)) { + //x-escape allows variable number of digits, from one to 4; let's count them + p = 1; //current position + while (p < 5 && p < segment.Length) { + if (Strings.HexDigits.IndexOf(segment[p]) < 0) break; + p++; + } + //p now point to char right after the last digit + if (p <= 1) { + details.Error = Resources.ErrBadXEscape; // @"Invalid \x escape, at least one digit expected."; + return segment; + } + digits = segment.Substring(1, p - 1); + ch = (char) Convert.ToUInt32(digits, 16); + result = ch + segment.Substring(p); + return result; + }//if + break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + if (details.IsSet((short)StringOptions.AllowsOctalEscapes)) { + //octal escape allows variable number of digits, from one to 3; let's count them + p = 0; //current position + while (p < 3 && p < segment.Length) { + if (Strings.OctalDigits.IndexOf(segment[p]) < 0) break; + p++; + } + //p now point to char right after the last digit + digits = segment.Substring(0, p); + ch = (char)Convert.ToUInt32(digits, 8); + result = ch + segment.Substring(p); + return result; + }//if + break; + }//switch + details.Error = string.Format(Resources.ErrInvEscape, segment); //"Invalid escape sequence: \{0}" + return segment; + }//method + #endregion + + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/TerminalFactory.cs b/Irony/Parsing/Terminals/TerminalFactory.cs new file mode 100644 index 0000000..d14a851 --- /dev/null +++ b/Irony/Parsing/Terminals/TerminalFactory.cs @@ -0,0 +1,176 @@ +#region License +/* ********************************************************************************** + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion +//Authors: Roman Ivantsov, Philipp Serr + +using System; +using System.Collections.Generic; +using System.Text; +using System.Globalization; + +namespace Irony.Parsing { + public static class TerminalFactory { + + public static StringLiteral CreateCSharpString(string name) { + StringLiteral term = new StringLiteral(name, "\"", StringOptions.AllowsAllEscapes); + term.AddPrefix("@", StringOptions.NoEscapes | StringOptions.AllowsLineBreak | StringOptions.AllowsDoubledQuote); + return term; + } + public static StringLiteral CreateCSharpChar(string name) { + StringLiteral term = new StringLiteral(name, "'", StringOptions.IsChar); + return term; + } + + public static StringLiteral CreateVbString(string name) { + StringLiteral term = new StringLiteral(name); + term.AddStartEnd("\"", StringOptions.NoEscapes | StringOptions.AllowsDoubledQuote); + term.AddSuffix("$", TypeCode.String); + term.AddSuffix("c", TypeCode.Char); + return term; + } + + public static StringLiteral CreatePythonString(string name) { + StringLiteral term = new StringLiteral(name); + term.AddStartEnd("'", StringOptions.AllowsAllEscapes); + term.AddStartEnd("'''", StringOptions.AllowsAllEscapes | StringOptions.AllowsLineBreak); + term.AddStartEnd("\"", StringOptions.AllowsAllEscapes); + term.AddStartEnd("\"\"\"", StringOptions.AllowsAllEscapes | StringOptions.AllowsLineBreak); + + term.AddPrefix("u", StringOptions.AllowsAllEscapes); + term.AddPrefix("r", StringOptions.NoEscapes ); + term.AddPrefix("ur", StringOptions.NoEscapes); + + return term; + } + + //http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf section 9.4.4 + public static NumberLiteral CreateCSharpNumber(string name) { + NumberLiteral term = new NumberLiteral(name); + term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64 }; + term.DefaultFloatType = TypeCode.Double; + term.AddPrefix("0x", NumberOptions.Hex); + term.AddSuffix("u", TypeCode.UInt32, TypeCode.UInt64); + term.AddSuffix("l", TypeCode.Int64, TypeCode.UInt64); + term.AddSuffix("ul", TypeCode.UInt64); + term.AddSuffix("f", TypeCode.Single); + term.AddSuffix("d", TypeCode.Double); + term.AddSuffix("m", TypeCode.Decimal); + return term; + } + //http://www.microsoft.com/downloads/details.aspx?FamilyId=6D50D709-EAA4-44D7-8AF3-E14280403E6E&displaylang=en section 2 + public static NumberLiteral CreateVbNumber(string name) { + NumberLiteral term = new NumberLiteral(name); + term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64 }; + //term.DefaultFloatType = TypeCode.Double; it is default + term.AddPrefix("&H", NumberOptions.Hex); + term.AddPrefix("&O", NumberOptions.Octal); + term.AddSuffix("S", TypeCode.Int16); + term.AddSuffix("I", TypeCode.Int32); + term.AddSuffix("%", TypeCode.Int32); + term.AddSuffix("L", TypeCode.Int64); + term.AddSuffix("&", TypeCode.Int64); + term.AddSuffix("D", TypeCode.Decimal); + term.AddSuffix("@", TypeCode.Decimal); + term.AddSuffix("F", TypeCode.Single); + term.AddSuffix("!", TypeCode.Single); + term.AddSuffix("R", TypeCode.Double); + term.AddSuffix("#", TypeCode.Double); + term.AddSuffix("US", TypeCode.UInt16); + term.AddSuffix("UI", TypeCode.UInt32); + term.AddSuffix("UL", TypeCode.UInt64); + return term; + } + //http://docs.python.org/ref/numbers.html + public static NumberLiteral CreatePythonNumber(string name) { + NumberLiteral term = new NumberLiteral(name, NumberOptions.AllowStartEndDot); + //default int types are Integer (32bit) -> LongInteger (BigInt); Try Int64 before BigInt: Better performance? + term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt }; + // term.DefaultFloatType = TypeCode.Double; -- it is default + //float type is implementation specific, thus try decimal first (higher precision) + //term.DefaultFloatTypes = new TypeCode[] { TypeCode.Decimal, TypeCode.Double }; + term.AddPrefix("0x", NumberOptions.Hex); + term.AddPrefix("0", NumberOptions.Octal); + term.AddSuffix("L", TypeCode.Int64, NumberLiteral.TypeCodeBigInt); + term.AddSuffix("J", NumberLiteral.TypeCodeImaginary); + return term; + } + + // About exponent symbols, extract from R6RS: + // ... representations of number objects may be written with an exponent marker that indicates the desired precision + // of the inexact representation. The letters s, f, d, and l specify the use of short, single, double, and long precision, respectively. + // ... + // In addition, the exponent marker e specifies the default precision for the implementation. The default precision + // has at least as much precision as double, but implementations may wish to allow this default to be set by the user. + public static NumberLiteral CreateSchemeNumber(string name) { + NumberLiteral term = new NumberLiteral(name); + term.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt }; + term.DefaultFloatType = TypeCode.Double; // it is default + term.AddExponentSymbols("eE", TypeCode.Double); //default precision for platform, double + term.AddExponentSymbols("sSfF", TypeCode.Single); + term.AddExponentSymbols("dDlL", TypeCode.Double); + term.AddPrefix("#b", NumberOptions.Binary); + term.AddPrefix("#o", NumberOptions.Octal); + term.AddPrefix("#x", NumberOptions.Hex); + term.AddPrefix("#d", NumberOptions.None); + term.AddPrefix("#i", NumberOptions.None); // inexact prefix, has no effect + term.AddPrefix("#e", NumberOptions.None); // exact prefix, has no effect + term.AddSuffix("J", NumberLiteral.TypeCodeImaginary); + return term; + } + + + public static IdentifierTerminal CreateCSharpIdentifier(string name) { + IdentifierTerminal id = new IdentifierTerminal(name, IdOptions.AllowsEscapes | IdOptions.CanStartWithEscape); + id.AddPrefix("@", IdOptions.IsNotKeyword); + //From spec: + //Start char is "_" or letter-character, which is a Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl + id.StartCharCategories.AddRange(new UnicodeCategory[] { + UnicodeCategory.UppercaseLetter, //Ul + UnicodeCategory.LowercaseLetter, //Ll + UnicodeCategory.TitlecaseLetter, //Lt + UnicodeCategory.ModifierLetter, //Lm + UnicodeCategory.OtherLetter, //Lo + UnicodeCategory.LetterNumber //Nl + }); + //Internal chars + /* From spec: + identifier-part-character: letter-character | decimal-digit-character | connecting-character | combining-character | + formatting-character +*/ + id.CharCategories.AddRange(id.StartCharCategories); //letter-character categories + id.CharCategories.AddRange(new UnicodeCategory[] { + UnicodeCategory.DecimalDigitNumber, //Nd + UnicodeCategory.ConnectorPunctuation, //Pc + UnicodeCategory.SpacingCombiningMark, //Mc + UnicodeCategory.NonSpacingMark, //Mn + UnicodeCategory.Format //Cf + }); + //Chars to remove from final identifier + id.CharsToRemoveCategories.Add(UnicodeCategory.Format); + return id; + } + + public static IdentifierTerminal CreatePythonIdentifier(string name) { + IdentifierTerminal id = new IdentifierTerminal("Identifier"); //defaults are OK + return id; + } + + //Covers simple identifiers like abcd, and also quoted versions: [abc d], "abc d". + public static IdentifierTerminal CreateSqlExtIdentifier(Grammar grammar, string name) { + var id = new IdentifierTerminal(name); + StringLiteral term = new StringLiteral(name + "_qouted"); + term.AddStartEnd("[", "]", StringOptions.NoEscapes); + term.AddStartEnd("\"", StringOptions.NoEscapes); + term.SetOutputTerminal(grammar, id); //term will be added to NonGrammarTerminals automatically + return id; + } + + }//class +}//namespace diff --git a/Irony/Parsing/Terminals/WikiTerminals/WikiBlockTerminal.cs b/Irony/Parsing/Terminals/WikiTerminals/WikiBlockTerminal.cs new file mode 100644 index 0000000..eda0e0c --- /dev/null +++ b/Irony/Parsing/Terminals/WikiTerminals/WikiBlockTerminal.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + public enum WikiBlockType { + EscapedText, + CodeBlock, + Anchor, + LinkToAnchor, + Url, + FileLink, //looks like it is the same as Url + Image, + } + + public class WikiBlockTerminal : WikiTerminalBase { + public readonly WikiBlockType BlockType; + + public WikiBlockTerminal(string name, WikiBlockType blockType, string openTag, string closeTag, string htmlElementName) + : base(name, WikiTermType.Block, openTag, closeTag, htmlElementName) { + BlockType = blockType; + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + if (!source.MatchSymbol(OpenTag)) return null; + source.PreviewPosition += OpenTag.Length; + var endPos = source.Text.IndexOf(CloseTag, source.PreviewPosition); + string content; + if(endPos > 0) { + content = source.Text.Substring(source.PreviewPosition, endPos - source.PreviewPosition); + source.PreviewPosition = endPos + CloseTag.Length; + } else { + content = source.Text.Substring(source.PreviewPosition, source.Text.Length - source.PreviewPosition); + source.PreviewPosition = source.Text.Length; + } + var token = source.CreateToken(this.OutputTerminal, content); + return token; + } + + + }//class +}//namespace diff --git a/Irony/Parsing/Terminals/WikiTerminals/WikiTagTerminal.cs b/Irony/Parsing/Terminals/WikiTerminals/WikiTagTerminal.cs new file mode 100644 index 0000000..5aa55f9 --- /dev/null +++ b/Irony/Parsing/Terminals/WikiTerminals/WikiTagTerminal.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + + //Handles formatting tags like *bold*, _italic_; also handles headings and lists + public class WikiTagTerminal : WikiTerminalBase { + + public WikiTagTerminal(string name, WikiTermType termType, string tag, string htmlElementName) + : this (name, termType, tag, string.Empty, htmlElementName) { } + + public WikiTagTerminal(string name, WikiTermType termType, string openTag, string closeTag, string htmlElementName) + : base (name, termType, openTag, closeTag, htmlElementName) { } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + bool isHeadingOrList = TermType == WikiTermType.Heading || TermType == WikiTermType.List; + if(isHeadingOrList) { + bool isAfterNewLine = (context.PreviousToken == null || context.PreviousToken.Terminal == Grammar.NewLine); + if(!isAfterNewLine) return null; + } + if(!source.MatchSymbol(OpenTag)) return null; + source.PreviewPosition += OpenTag.Length; + //For headings and lists require space after + if(TermType == WikiTermType.Heading || TermType == WikiTermType.List) { + const string whitespaces = " \t\r\n\v"; + if (!whitespaces.Contains(source.PreviewChar)) return null; + } + var token = source.CreateToken(this.OutputTerminal); + return token; + } + + }//class + +}//namespace diff --git a/Irony/Parsing/Terminals/WikiTerminals/WikiTextTerminal.cs b/Irony/Parsing/Terminals/WikiTerminals/WikiTextTerminal.cs new file mode 100644 index 0000000..dd68d26 --- /dev/null +++ b/Irony/Parsing/Terminals/WikiTerminals/WikiTextTerminal.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + //Handles plain text + public class WikiTextTerminal : WikiTerminalBase { + public const char NoEscape = '\0'; + public char EscapeChar = NoEscape; + private char[] _stopChars; + + public WikiTextTerminal(string name) : base(name, WikiTermType.Text, string.Empty, string.Empty, string.Empty) { + this.Priority = TerminalPriority.Low; + } + + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + var stopCharSet = new CharHashSet(); + foreach(var term in grammarData.Terminals) { + var firsts = term.GetFirsts(); + if (firsts == null) continue; + foreach (var first in firsts) + if (!string.IsNullOrEmpty(first)) + stopCharSet.Add(first[0]); + }//foreach term + if (EscapeChar != NoEscape) + stopCharSet.Add(EscapeChar); + _stopChars = stopCharSet.ToArray(); + } + + //override to WikiTerminalBase's method to return null, indicating there are no firsts, so it is a fallback terminal + public override IList GetFirsts() { + return null; + } + + public override Token TryMatch(ParsingContext context, ISourceStream source) { + bool isEscape = source.PreviewChar == EscapeChar && EscapeChar != NoEscape; + if(isEscape) { + //return a token containing only escaped char + var value = source.NextPreviewChar.ToString(); + source.PreviewPosition += 2; + return source.CreateToken(this.OutputTerminal, value); + } + var stopIndex = source.Text.IndexOfAny(_stopChars, source.Location.Position + 1); + if (stopIndex == source.Location.Position) return null; + if (stopIndex < 0) stopIndex = source.Text.Length; + source.PreviewPosition = stopIndex; + return source.CreateToken(this.OutputTerminal); + }//method + + }//class + +} diff --git a/Irony/Parsing/Terminals/WikiTerminals/_WikiTerminalBase.cs b/Irony/Parsing/Terminals/WikiTerminals/_WikiTerminalBase.cs new file mode 100644 index 0000000..fcead75 --- /dev/null +++ b/Irony/Parsing/Terminals/WikiTerminals/_WikiTerminalBase.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + public enum WikiTermType { + Text, + Element, + Format, + Heading, + List, + Block, + Table + } + + public abstract class WikiTerminalBase : Terminal { + public readonly WikiTermType TermType; + public readonly string OpenTag, CloseTag; + public string HtmlElementName, ContainerHtmlElementName; + public string OpenHtmlTag, CloseHtmlTag; + public string ContainerOpenHtmlTag, ContainerCloseHtmlTag; + + public WikiTerminalBase(string name, WikiTermType termType, string openTag, string closeTag, string htmlElementName) : base(name) { + TermType = termType; + OpenTag = openTag; + CloseTag = closeTag; + HtmlElementName = htmlElementName; + this.Priority = TerminalPriority.Normal + OpenTag.Length; //longer tags have higher priority + } + + public override IList GetFirsts() { + return new string[] {OpenTag}; + } + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + if (!string.IsNullOrEmpty(HtmlElementName)) { + if (string.IsNullOrEmpty(OpenHtmlTag)) OpenHtmlTag = "<" + HtmlElementName + ">"; + if (string.IsNullOrEmpty(CloseHtmlTag)) CloseHtmlTag = ""; + } + if (!string.IsNullOrEmpty(ContainerHtmlElementName)) { + if (string.IsNullOrEmpty(ContainerOpenHtmlTag)) ContainerOpenHtmlTag = "<" + ContainerHtmlElementName + ">"; + if (string.IsNullOrEmpty(ContainerCloseHtmlTag)) ContainerCloseHtmlTag = ""; + } + + } + + }//class + + + +}//namespace diff --git a/Irony/Parsing/Terminals/_Terminal.cs b/Irony/Parsing/Terminals/_Terminal.cs new file mode 100644 index 0000000..fd03303 --- /dev/null +++ b/Irony/Parsing/Terminals/_Terminal.cs @@ -0,0 +1,146 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + public static class TerminalPriority { + public static int Low = -1000; + public static int Normal = 0; + public static int High = 1000; + public static int ReservedWords = 900; + + } + + public class Terminal : BnfTerm { + #region Constructors + public Terminal(string name) : this(name, TokenCategory.Content, TermFlags.None) { } + public Terminal(string name, TokenCategory category) : this(name, category, TermFlags.None) { } + public Terminal(string name, string errorAlias, TokenCategory category, TermFlags flags) : this(name, category, flags) { + this.ErrorAlias = errorAlias; + } + public Terminal(string name, TokenCategory category, TermFlags flags) : base(name) { + Category = category; + this.Flags |= flags; + if (Category == TokenCategory.Outline) + this.SetFlag(TermFlags.IsPunctuation); + OutputTerminal = this; + } + #endregion + + #region fields and properties + public TokenCategory Category = TokenCategory.Content; + // Priority is used when more than one terminal may match the input char. + // It determines the order in which terminals will try to match input for a given char in the input. + // For a given input char the scanner uses the hash table to look up the collection of terminals that may match this input symbol. + // It is the order in this collection that is determined by Priority property - the higher the priority, + // the earlier the terminal gets a chance to check the input. + public int Priority = TerminalPriority.Normal; //default is 0 + + //Terminal to attach to the output token. By default is set to the Terminal itself + // Use SetOutputTerminal method to change it. For example of use see TerminalFactory.CreateSqlIdentifier and sample SQL grammar + public Terminal OutputTerminal { get; protected set; } + + public TokenEditorInfo EditorInfo; + public byte MultilineIndex; + public Terminal IsPairFor; + #endregion + + #region virtual methods: GetFirsts(), TryMatch, Init, TokenToString + public override void Init(GrammarData grammarData) { + base.Init(grammarData); + } + + //"Firsts" (chars) collections are used for quick search for possible matching terminal(s) using current character in the input stream. + // A terminal might declare no firsts. In this case, the terminal is tried for match for any current input character. + public virtual IList GetFirsts() { + return null; + } + + public virtual Token TryMatch(ParsingContext context, ISourceStream source) { + return null; + } + + public virtual string TokenToString(Token token) { + if (token.ValueString == this.Name) + return token.ValueString; + else + return (token.ValueString ?? token.Text) + " (" + Name + ")"; + } + + + #endregion + + #region Events: ValidateToken, ParserInputPreview + public event EventHandler ValidateToken; + protected internal virtual void OnValidateToken(ParsingContext context) { + if (ValidateToken != null) + ValidateToken(this, context.SharedValidateTokenEventArgs); + } + + //Invoked when ParseTreeNode is created from the token. This is parser-preview event, when parser + // just received the token, wrapped it into ParseTreeNode and is about to look at it. + public event EventHandler ParserInputPreview; + protected internal virtual void OnParserInputPreview(ParsingContext context) { + if (ParserInputPreview != null) + ParserInputPreview(this, context.SharedParsingEventArgs); + } + #endregion + + #region static comparison methods + public static int ByPriorityReverse(Terminal x, Terminal y) { + if (x.Priority > y.Priority) + return -1; + if (x.Priority == y.Priority) + return 0; + return 1; + } + #endregion + + #region Miscellaneous: SetOutputTerminal + public void SetOutputTerminal(Grammar grammar, Terminal outputTerminal) { + OutputTerminal = outputTerminal; + grammar.NonGrammarTerminals.Add(this); + } + + #endregion + //Priority constants + [Obsolete("Deprecated: use constants in TerminalPriority class instead")] + public const int LowestPriority = -1000; + [Obsolete("Deprecated: use constants in TerminalPriority class instead")] + public const int HighestPriority = 1000; + [Obsolete("Deprecated: use constants in TerminalPriority class instead")] + public const int ReservedWordsPriority = 900; //almost top one + + public static string TerminalsToString(IEnumerable terminals) { + return string.Join(" ", terminals); + } + + }//class + + public class TerminalSet : HashSet { + public override string ToString() { + return Terminal.TerminalsToString(this); + } + } + + public class TerminalList : List { + public override string ToString() { + return Terminal.TerminalsToString(this); + } + } + + +}//namespace diff --git a/Irony/Parsing/TokenFilters/CodeOutlineFilter.cs b/Irony/Parsing/TokenFilters/CodeOutlineFilter.cs new file mode 100644 index 0000000..2c382dd --- /dev/null +++ b/Irony/Parsing/TokenFilters/CodeOutlineFilter.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + [Flags] + public enum OutlineOptions { + None = 0, + ProduceIndents = 0x01, + CheckBraces = 0x02, + CheckOperator = 0x04, //to implement, auto line joining if line ends with operator + } + + public class CodeOutlineFilter : TokenFilter { + + public readonly OutlineOptions Options; + public readonly KeyTerm ContinuationTerminal; //Terminal + + GrammarData _grammarData; + Grammar _grammar; + ParsingContext _context; + bool _produceIndents; + bool _checkBraces, _checkOperator; + + public Stack Indents = new Stack(); + public Token CurrentToken; + public Token PreviousToken; + public SourceLocation PreviousTokenLocation; + public TokenStack OutputTokens = new TokenStack(); + bool _isContinuation, _prevIsContinuation; + bool _isOperator, _prevIsOperator; + bool _doubleEof; + + #region constructor + public CodeOutlineFilter(GrammarData grammarData, OutlineOptions options, KeyTerm continuationTerminal) { + _grammarData = grammarData; + _grammar = grammarData.Grammar; + _grammar.LanguageFlags |= LanguageFlags.EmitLineStartToken; + Options = options; + ContinuationTerminal = continuationTerminal; + if (ContinuationTerminal != null) + if (!_grammar.NonGrammarTerminals.Contains(ContinuationTerminal)) + _grammarData.Language.Errors.Add(GrammarErrorLevel.Warning, null, Resources.ErrOutlineFilterContSymbol, ContinuationTerminal.Name); + //"CodeOutlineFilter: line continuation symbol '{0}' should be added to Grammar.NonGrammarTerminals list.", + _produceIndents = OptionIsSet(OutlineOptions.ProduceIndents); + _checkBraces = OptionIsSet(OutlineOptions.CheckBraces); + _checkOperator = OptionIsSet(OutlineOptions.CheckOperator); + Reset(); + } + #endregion + + public override void Reset() { + base.Reset(); + Indents.Clear(); + Indents.Push(0); + OutputTokens.Clear(); + PreviousToken = null; + CurrentToken = null; + PreviousTokenLocation = new SourceLocation(); + } + + public bool OptionIsSet(OutlineOptions option) { + return (Options & option) != 0; + } + + public override IEnumerable BeginFiltering(ParsingContext context, IEnumerable tokens) { + _context = context; + foreach (Token token in tokens) { + ProcessToken(token); + while (OutputTokens.Count > 0) + yield return OutputTokens.Pop(); + }//foreach + }//method + + public void ProcessToken(Token token) { + SetCurrentToken(token); + //Quick checks + if (_isContinuation) + return; + var tokenTerm = token.Terminal; + + //check EOF + if (tokenTerm == _grammar.Eof) { + ProcessEofToken(); + return; + } + + if (tokenTerm != _grammar.LineStartTerminal) return; + //if we are here, we have LineStart token on new line; first remove it from stream, it should not go to parser + OutputTokens.Pop(); + + if (PreviousToken == null) return; + + + // first check if there was continuation symbol before + // or - if checkBraces flag is set - check if there were open braces + if (_prevIsContinuation || _checkBraces && _context.OpenBraces.Count > 0) + return; //no Eos token in this case + if (_prevIsOperator && _checkOperator) + return; //no Eos token in this case + + //We need to produce Eos token and indents (if _produceIndents is set). + // First check indents - they go first into OutputTokens stack, so they will be popped out last + if (_produceIndents) { + var currIndent = token.Location.Column; + var prevIndent = Indents.Peek(); + if (currIndent > prevIndent) { + Indents.Push(currIndent); + PushOutlineToken(_grammar.Indent, token.Location); + } else if (currIndent < prevIndent) { + PushDedents(currIndent); + //check that current indent exactly matches the previous indent + if (Indents.Peek() != currIndent) { + //fire error + OutputTokens.Push(new Token(_grammar.SyntaxError, token.Location, string.Empty, Resources.ErrInvDedent)); + // "Invalid dedent level, no previous matching indent found." + } + } + }//if _produceIndents + //Finally produce Eos token, but not in command line mode. In command line mode the Eos was already produced + // when we encountered Eof on previous line + if (_context.Mode != ParseMode.CommandLine) { + var eosLocation = ComputeEosLocation(); + PushOutlineToken(_grammar.Eos, eosLocation); + } + + }//method + + private void SetCurrentToken(Token token) { + _doubleEof = CurrentToken != null && CurrentToken.Terminal == _grammar.Eof + && token.Terminal == _grammar.Eof; + //Copy CurrentToken to PreviousToken + if (CurrentToken != null && CurrentToken.Category == TokenCategory.Content) { //remember only content tokens + PreviousToken = CurrentToken; + _prevIsContinuation = _isContinuation; + _prevIsOperator = _isOperator; + if (PreviousToken != null) + PreviousTokenLocation = PreviousToken.Location; + } + CurrentToken = token; + _isContinuation = (token.Terminal == ContinuationTerminal && ContinuationTerminal != null); + _isOperator = token.Terminal.Flags.IsSet(TermFlags.IsOperator); + if (!_isContinuation) + OutputTokens.Push(token); //by default input token goes to output, except continuation symbol + } + + //Processes Eof token. We should take into account the special case of processing command line input. + // In this case we should not automatically dedent all stacked indents if we get EOF. + // Note that tokens will be popped from the OutputTokens stack and sent to parser in the reverse order compared to + // the order we pushed them into OutputTokens stack. We have Eof already in stack; we first push dedents, then Eos + // They will come out to parser in the following order: Eos, Dedents, Eof. + private void ProcessEofToken() { + //First decide whether we need to produce dedents and Eos symbol + bool pushDedents = false; + bool pushEos = true; + switch (_context.Mode) { + case ParseMode.File: + pushDedents = _produceIndents; //Do dedents if token filter tracks indents + break; + case ParseMode.CommandLine: + //only if user entered empty line, we dedent all + pushDedents = _produceIndents && _doubleEof; + pushEos = !_prevIsContinuation && !_doubleEof; //if previous symbol is continuation symbol then don't push Eos + break; + case ParseMode.VsLineScan: + pushDedents = false; //Do not dedent at all on every line end + break; + } + //unindent all buffered indents; + if (pushDedents) PushDedents(0); + //now push Eos token - it will be popped first, then dedents, then EOF token + if (pushEos) { + var eosLocation = ComputeEosLocation(); + PushOutlineToken(_grammar.Eos, eosLocation); + } + } + + private void PushDedents(int untilPosition) { + while (Indents.Peek() > untilPosition) { + Indents.Pop(); + PushOutlineToken(_grammar.Dedent, CurrentToken.Location); + } + } + + private SourceLocation ComputeEosLocation() { + if (PreviousToken == null) + return new SourceLocation(); + //Return position at the end of previous token + var loc = PreviousToken.Location; + var len = PreviousToken.Length; + return new SourceLocation(loc.Position + len, loc.Line, loc.Column + len); + } + + private void PushOutlineToken(Terminal term, SourceLocation location) { + OutputTokens.Push(new Token(term, location, string.Empty, null)); + } + + }//class +}//namespace diff --git a/Irony/Parsing/TokenFilters/TokenFilter.cs b/Irony/Parsing/TokenFilters/TokenFilter.cs new file mode 100644 index 0000000..448bf05 --- /dev/null +++ b/Irony/Parsing/TokenFilters/TokenFilter.cs @@ -0,0 +1,55 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony.Parsing { + + #region Comments + // Token filter is a token preprocessor that operates on a token stream between scanner and parser: + // scanner -> (token filters)-> parser + // Token stream from scanner output is fed into a chain of token filters that add/remove/modify tokens + // in the stream before it gets to the parser. Some tasks that come up in scanning and parsing are best + // handled by such an intermediate processor. Examples: + // * Macro expansion + // * Conditional compilation clauses + // * Handling commented-out blocks. Scheme allows commenting out entire blocks of code using "#;" prefix followed by + // well-formed datum. This type of comments cannot be handled by scanner as it requires parser-like processing + // of the stream to locate the end of the block. At the same time parser is not a good place to handle this either, + // as it would require defining optional "commented block" element everywhere in the grammar. + // Token filter is an ideal place for implementing this task - after scanning but before parsing. + // * Assembling doc-comment blocks (XML doc lines in c#) from individual comment lines + // and attaching it to the next content token, and later sticking it to the parsed node. + // * Handling newlines, indents and unindents for languages like Python. + // Tracking this information directly in the scanner makes things really messy, and it does not fit well + // into general-purpose scanner. Token filter can handle it easily. In this case the scanner + // handles the new-line character and indentations as whitespace and simply ignores it. + // The CodeOutlineFilter re-creates new-line and indent tokens by analyzing + // the line/column properties of the incoming tokens, and inserts them into its output. + #endregion + public class TokenFilter { + + public virtual IEnumerable BeginFiltering(ParsingContext context, IEnumerable tokens) { + yield break; + } + + public virtual void Reset() { + } + protected internal virtual void OnSetSourceLocation(SourceLocation location) { + } + }//class + + public class TokenFilterList : List { } + +}//namespace diff --git a/Irony/Properties/AssemblyInfo.cs b/Irony/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dd58a40 --- /dev/null +++ b/Irony/Properties/AssemblyInfo.cs @@ -0,0 +1,60 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +#if !SILVERLIGHT +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("44015759-db10-4a6f-8251-d1d18599b60f")] +[assembly: AssemblyTitle("Irony")] +[assembly: AssemblyDescription("Irony Main Assembly")] +[assembly: AssemblyConfiguration("")] +#else +[assembly: Guid("B83C8EBA-E4E5-4761-9C38-F662F56D63D7")] +[assembly: AssemblyTitle("Irony-SL")] +[assembly: AssemblyDescription("Irony for Silverlight")] +[assembly: AssemblyConfiguration("")] +#endif +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Irony")] +[assembly: AssemblyCopyright("Copyright © 2011 Roman Ivantsov")] +[assembly: AssemblyTrademark("Irony")] +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] + +//Make the code security-transparent. more info here: http://msdn.microsoft.com/en-us/library/bb397858.aspx +[assembly: SecurityTransparent()] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Irony/Resources.Designer.cs b/Irony/Resources.Designer.cs new file mode 100644 index 0000000..5e1d5e4 --- /dev/null +++ b/Irony/Resources.Designer.cs @@ -0,0 +1,1071 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.239 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Irony { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Irony.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Nn. + /// + public static string ConsoleNoChars { + get { + return ResourceManager.GetString("ConsoleNoChars", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yy. + /// + public static string ConsoleYesChars { + get { + return ResourceManager.GetString("ConsoleYesChars", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ambiguous grammar, unresolvable reduce-reduce conflicts. State {0}, lookaheads [{1}]. + /// + public static string ErrAmbigGrammarRR { + get { + return ResourceManager.GetString("ErrAmbigGrammarRR", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ambiguous grammar, unresolvable shift-reduce conflicts. State {0}, lookaheads [{1}]. + /// + public static string ErrAmbigGrammarSR { + get { + return ResourceManager.GetString("ErrAmbigGrammarSR", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Argument list not found in the stack. Expected: ValueList, found: {0}.. + /// + public static string ErrArgListNotFound { + get { + return ResourceManager.GetString("ErrArgListNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid operation, attempt to assign to a constant or literal value.. + /// + public static string ErrAssignLiteralValue { + get { + return ResourceManager.GetString("ErrAssignLiteralValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid length of char literal - should be a single character.. + /// + public static string ErrBadChar { + get { + return ResourceManager.GetString("ErrBadChar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Mal-formed string literal - cannot find termination symbol.. + /// + public static string ErrBadStrLiteral { + get { + return ResourceManager.GetString("ErrBadStrLiteral", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid unicode escape ({0}), expected {1} hex digits.. + /// + public static string ErrBadUnEscape { + get { + return ResourceManager.GetString("ErrBadUnEscape", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid \x escape, at least one digit expected.. + /// + public static string ErrBadXEscape { + get { + return ResourceManager.GetString("ErrBadXEscape", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot convert value from type {0} to type {1}, type converter not defined.. + /// + public static string ErrCannotConvertValue { + get { + return ResourceManager.GetString("ErrCannotConvertValue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot convert literal {0} to type {1}.. + /// + public static string ErrCannotConvertValueToType { + get { + return ResourceManager.GetString("ErrCannotConvertValueToType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fatal error in parser: attempt to move back in the source.. + /// + public static string ErrCannotMoveBackInSource { + get { + return ResourceManager.GetString("ErrCannotMoveBackInSource", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} State {1} on inputs: {2}. + /// + public static string ErrConflictMsgTemplate { + get { + return ResourceManager.GetString("ErrConflictMsgTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fatal error:. + /// + public static string ErrConsoleFatalError { + get { + return ResourceManager.GetString("ErrConsoleFatalError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Construct '{0}' is not supported (yet) by language implementation.. + /// + public static string ErrConstructNotSupported { + get { + return ResourceManager.GetString("ErrConstructNotSupported", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Could not find a closing quote for quoted value.. + /// + public static string ErrDsvNoClosingQuote { + get { + return ResourceManager.GetString("ErrDsvNoClosingQuote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate switch '{0}' for regular expression.. + /// + public static string ErrDupRegexSwitch { + get { + return ResourceManager.GetString("ErrDupRegexSwitch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Duplicate start symbol {0} in string literal [{1}].. + /// + public static string ErrDupStartSymbolStr { + get { + return ResourceManager.GetString("ErrDupStartSymbolStr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to create AST node for non-terminal [{0}], error: {1}. + /// + public static string ErrFailedCreateNode { + get { + return ResourceManager.GetString("ErrFailedCreateNode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to find the ending tag '{0}' for a text literal. . + /// + public static string ErrFreeTextNoEndTag { + get { + return ResourceManager.GetString("ErrFreeTextNoEndTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ImpliedSymbolTerminal cannot be used in grammar with DisableScannerParserLink flag set. + /// + public static string ErrImpliedOpUseParserLink { + get { + return ResourceManager.GetString("ErrImpliedOpUseParserLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Interpreter error, DataStack.Pop() operation failed - stack is empty.. + /// + public static string ErrInternalErrDataPopFailed { + get { + return ResourceManager.GetString("ErrInternalErrDataPopFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Interpreter is busy.. + /// + public static string ErrInterpreterIsBusy { + get { + return ResourceManager.GetString("ErrInterpreterIsBusy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid arguments for IncDecNode AST node: either first or second argument should be '--' or '++'.. + /// + public static string ErrInvalidArgsForIncDec { + get { + return ResourceManager.GetString("ErrInvalidArgsForIncDec", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid AstMode value in call to Evaluate method. Node: {0}, mode: {1}.. + /// + public static string ErrInvalidAstMode { + get { + return ResourceManager.GetString("ErrInvalidAstMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid character: '{0}'.. + /// + public static string ErrInvalidChar { + get { + return ResourceManager.GetString("ErrInvalidChar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid embedded expression. . + /// + public static string ErrInvalidEmbeddedPrefix { + get { + return ResourceManager.GetString("ErrInvalidEmbeddedPrefix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid dedent level, no previous matching indent found.. + /// + public static string ErrInvDedent { + get { + return ResourceManager.GetString("ErrInvDedent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid escape sequence: \{0}.. + /// + public static string ErrInvEscape { + get { + return ResourceManager.GetString("ErrInvEscape", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid escape sequence.. + /// + public static string ErrInvEscSeq { + get { + return ResourceManager.GetString("ErrInvEscSeq", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid escape symbol, expected 'u' or 'U' only.. + /// + public static string ErrInvEscSymbol { + get { + return ResourceManager.GetString("ErrInvEscSymbol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid number.. + /// + public static string ErrInvNumber { + get { + return ResourceManager.GetString("ErrInvNumber", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid switch '{0}' for regular expression. + /// + public static string ErrInvRegexSwitch { + get { + return ResourceManager.GetString("ErrInvRegexSwitch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error in string literal [{0}]: No start/end symbols specified.. + /// + public static string ErrInvStrDef { + get { + return ResourceManager.GetString("ErrInvStrDef", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The last term of production containing SyntaxError must be a terminal. NonTerminal: {0}. + /// + public static string ErrLastTermOfErrorProd { + get { + return ResourceManager.GetString("ErrLastTermOfErrorProd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List non-terminals cannot be marked transient; list: ({0}). + /// + public static string ErrListCannotBeTransient { + get { + return ResourceManager.GetString("ErrListCannotBeTransient", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expected new line symbol.. + /// + public static string ErrNewLineExpected { + get { + return ResourceManager.GetString("ErrNewLineExpected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No closing pair for opening symbol {0}. + /// + public static string ErrNoClosingBrace { + get { + return ResourceManager.GetString("ErrNoClosingBrace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to AstNodeType or AstNodeCreator is not set on non-terminals: {0}. Either set Term.AstConfig.NodeType, or provide default values in AstContext.. + /// + public static string ErrNodeTypeNotSetOn { + get { + return ResourceManager.GetString("ErrNodeTypeNotSetOn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No end symbol for regex literal.. + /// + public static string ErrNoEndForRegex { + get { + return ResourceManager.GetString("ErrNoEndForRegex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No ending tag '{0}' found in embedded expression.. + /// + public static string ErrNoEndTagInEmbExpr { + get { + return ResourceManager.GetString("ErrNoEndTagInEmbExpr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to UnExprNode: no implementation for unary operator '{0}'.. + /// + public static string ErrNoImplForUnaryOp { + get { + return ResourceManager.GetString("ErrNoImplForUnaryOp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Number cannot be followed by a letter.. + /// + public static string ErrNoLetterAfterNum { + get { + return ResourceManager.GetString("ErrNoLetterAfterNum", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ParserDataBuilder error: inadequate state {0}, reduce item '{1}' has no lookaheads.. + /// + public static string ErrNoLkhds { + get { + return ResourceManager.GetString("ErrNoLkhds", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Non-terminal {0} has uninitialized Rule property.. + /// + public static string ErrNtRuleIsNull { + get { + return ResourceManager.GetString("ErrNtRuleIsNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Attempt to evaluate NULL AST node. The AST node for term '{0}' was not created during parsing.. + /// + public static string ErrNullNodeEval { + get { + return ResourceManager.GetString("ErrNullNodeEval", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Operator '{0}' is not defined for types {1}.. + /// + public static string ErrOpNotDefinedForType { + get { + return ResourceManager.GetString("ErrOpNotDefinedForType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Operator '{0}' is not defined for types {1} and {2}.. + /// + public static string ErrOpNotDefinedForTypes { + get { + return ResourceManager.GetString("ErrOpNotDefinedForTypes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Operator '{0} not imlemented.. + /// + public static string ErrOpNotImplemented { + get { + return ResourceManager.GetString("ErrOpNotImplemented", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}: {1}. + /// + public static string ErrOutErrorPrintFormat { + get { + return ResourceManager.GetString("ErrOutErrorPrintFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CodeOutlineFilter: line continuation symbol '{0}' should be added to Grammar.NonGrammarTerminals list.. + /// + public static string ErrOutlineFilterContSymbol { + get { + return ResourceManager.GetString("ErrOutlineFilterContSymbol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Syntax error, unexpected input.. + /// + public static string ErrParserUnexpectedInput { + get { + return ResourceManager.GetString("ErrParserUnexpectedInput", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parsed tree is null, cannot evaluate.. + /// + public static string ErrParseTreeNull { + get { + return ResourceManager.GetString("ErrParseTreeNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parse tree root is null, cannot evaluate.. + /// + public static string ErrParseTreeRootNull { + get { + return ResourceManager.GetString("ErrParseTreeRootNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Root AST node is null, cannot evaluate.. + /// + public static string ErrRootAstNodeNull { + get { + return ResourceManager.GetString("ErrRootAstNodeNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Root AST node does not implement IInterpretedAstNode interface, cannot evaluate.. + /// + public static string ErrRootAstNoInterface { + get { + return ResourceManager.GetString("ErrRootAstNoInterface", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ({0}) term passed as 'root' paramater to parserr is not Root or snippet root of the grammar. Add it to SnippetRoots set in grammar constructor.. + /// + public static string ErrRootNotRegistered { + get { + return ResourceManager.GetString("ErrRootNotRegistered", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Root property of the grammar is not set.. + /// + public static string ErrRootNotSet { + get { + return ResourceManager.GetString("ErrRootNotSet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reduce-reduce conflict. State {0}, lookaheads: {1}. Selected reduce on first production in conflict set.. + /// + public static string ErrRRConflict { + get { + return ResourceManager.GetString("ErrRRConflict", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rule for NonTerminal {0} contains null as an operand in position {1} in one of productions.. + /// + public static string ErrRuleContainsNull { + get { + return ResourceManager.GetString("ErrRuleContainsNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shift-reduce conflict. State {0}, lookaheads [{1}]. Selected shift as preferred action.. + /// + public static string ErrSRConflict { + get { + return ResourceManager.GetString("ErrSRConflict", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Syntax error, expected: {0}. + /// + public static string ErrSyntaxErrorExpected { + get { + return ResourceManager.GetString("ErrSyntaxErrorExpected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Syntax error.. + /// + public static string ErrSyntaxErrorNoInfo { + get { + return ResourceManager.GetString("ErrSyntaxErrorNoInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expression root non-terminal in template settings (AstNodeConfig property) in templated string literal [{0}] is not added to Roots set. Add it to SnippetRoots in grammar constructor.. + /// + public static string ErrTemplExprNotRoot { + get { + return ResourceManager.GetString("ErrTemplExprNotRoot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expression root is not specified in template settings (AstNodeConfig property) in templated string literal [{0}]. . + /// + public static string ErrTemplMissingExprRoot { + get { + return ResourceManager.GetString("ErrTemplMissingExprRoot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error in string literal [{0}]: IsTemplate flag is set, but TemplateSettings is not provided in AstNodeConfig property.. + /// + public static string ErrTemplNoSettings { + get { + return ResourceManager.GetString("ErrTemplNoSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A terminal {0} has empty prefix.. + /// + public static string ErrTerminalHasEmptyPrefix { + get { + return ResourceManager.GetString("ErrTerminalHasEmptyPrefix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Transient non-terminal must have zero or one non-punctuation child nodes; non-terminals: {0}.. + /// + public static string ErrTransientNtMustHaveOneTerm { + get { + return ResourceManager.GetString("ErrTransientNtMustHaveOneTerm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unclosed comment block. + /// + public static string ErrUnclosedComment { + get { + return ResourceManager.GetString("ErrUnclosedComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unexpected end of file.. + /// + public static string ErrUnexpEof { + get { + return ResourceManager.GetString("ErrUnexpEof", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unexpected indentation.. + /// + public static string ErrUnexpIndent { + get { + return ResourceManager.GetString("ErrUnexpIndent", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unmatched closing brace '{0}'.. + /// + public static string ErrUnmatchedCloseBrace { + get { + return ResourceManager.GetString("ErrUnmatchedCloseBrace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Variable {0} is not a callable function.. + /// + public static string ErrVarIsNotCallable { + get { + return ResourceManager.GetString("ErrVarIsNotCallable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Variable {0} not defined.. + /// + public static string ErrVarNotDefined { + get { + return ResourceManager.GetString("ErrVarNotDefined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid number of arguments. Expected {0}, found {1}.. + /// + public static string ErrWrongArgCount { + get { + return ResourceManager.GetString("ErrWrongArgCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ({0}:{1}). + /// + public static string FmtRowCol { + get { + return ResourceManager.GetString("FmtRowCol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Accept. + /// + public static string LabelActionAccept { + get { + return ResourceManager.GetString("LabelActionAccept", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Operator, shift to {0}/reduce on {1}.. + /// + public static string LabelActionOp { + get { + return ResourceManager.GetString("LabelActionOp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reduce on {0}. + /// + public static string LabelActionReduce { + get { + return ResourceManager.GetString("LabelActionReduce", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shift to {0}. + /// + public static string LabelActionShift { + get { + return ResourceManager.GetString("LabelActionShift", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (Unknown action type). + /// + public static string LabelActionUnknown { + get { + return ResourceManager.GetString("LabelActionUnknown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (EOF). + /// + public static string LabelEofMark { + get { + return ResourceManager.GetString("LabelEofMark", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [end-of-statement]. + /// + public static string LabelEosLabel { + get { + return ResourceManager.GetString("LabelEosLabel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (INITIAL STATE). + /// + public static string LabelInitialState { + get { + return ResourceManager.GetString("LabelInitialState", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (Key symbol). + /// + public static string LabelKeySymbol { + get { + return ResourceManager.GetString("LabelKeySymbol", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (Keyword). + /// + public static string LabelKeyword { + get { + return ResourceManager.GetString("LabelKeyword", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [line break]. + /// + public static string LabelLineBreak { + get { + return ResourceManager.GetString("LabelLineBreak", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Location:. + /// + public static string LabelLocation { + get { + return ResourceManager.GetString("LabelLocation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to None. + /// + public static string LabelNone { + get { + return ResourceManager.GetString("LabelNone", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to .... + /// + public static string LabelSrcHaveMore { + get { + return ResourceManager.GetString("LabelSrcHaveMore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to (unnamed). + /// + public static string LabelUnnamed { + get { + return ResourceManager.GetString("LabelUnnamed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Abort script(y/n)?. + /// + public static string MsgAbortScriptYN { + get { + return ResourceManager.GetString("MsgAbortScriptYN", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} Console.\r\nPress Ctrl-C to exit the program.\r\n. + /// + public static string MsgDefaultConsoleGreeting { + get { + return ResourceManager.GetString("MsgDefaultConsoleGreeting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Console. + /// + public static string MsgDefaultConsoleTitle { + get { + return ResourceManager.GetString("MsgDefaultConsoleTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom action did not execute: parser state or input did not change.. + /// + public static string MsgErrorCustomActionDidNotAdvance { + get { + return ResourceManager.GetString("MsgErrorCustomActionDidNotAdvance", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Exit console (y/n)?. + /// + public static string MsgExitConsoleYN { + get { + return ResourceManager.GetString("MsgExitConsoleYN", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to NLALR transform: Add WrapTail() in '.' position to [{0}].. + /// + public static string MsgNLALRAdvice { + get { + return ResourceManager.GetString("MsgNLALRAdvice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Press any key to end the program.. + /// + public static string MsgPressAnyKeyToExit { + get { + return ResourceManager.GetString("MsgPressAnyKeyToExit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "[{0}], at {1}. + /// + public static string MsgSrcPosToString { + get { + return ResourceManager.GetString("MsgSrcPosToString", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parsing conflict resolved in code.. + /// + public static string MsgTraceConflictResolved { + get { + return ResourceManager.GetString("MsgTraceConflictResolved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Executing custom action. + /// + public static string MsgTraceExecCustomAction { + get { + return ResourceManager.GetString("MsgTraceExecCustomAction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Operator - resolved to {0}. + /// + public static string MsgTraceOpResolved { + get { + return ResourceManager.GetString("MsgTraceOpResolved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Popped state from stack, pushing {0}. + /// + public static string MsgTracePoppedState { + get { + return ResourceManager.GetString("MsgTracePoppedState", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RECOVERING: {0}. + /// + public static string MsgTraceRecoverAction { + get { + return ResourceManager.GetString("MsgTraceRecoverAction", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to FAILED TO RECOVER. + /// + public static string MsgTraceRecoverFailed { + get { + return ResourceManager.GetString("MsgTraceRecoverFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RECOVERING: Found state with shift on error : {0}. + /// + public static string MsgTraceRecoverFoundState { + get { + return ResourceManager.GetString("MsgTraceRecoverFoundState", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RECOVERING: popping stack, looking for state with error shift. + /// + public static string MsgTraceRecovering { + get { + return ResourceManager.GetString("MsgTraceRecovering", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RECOVERING: Reached end of error production, reducing.. + /// + public static string MsgTraceRecoverReducing { + get { + return ResourceManager.GetString("MsgTraceRecoverReducing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RECOVERING: Shifting Error term, {0}. + /// + public static string MsgTraceRecoverShiftError { + get { + return ResourceManager.GetString("MsgTraceRecoverShiftError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RECOVERING: shifting until the end of error production.. + /// + public static string MsgTraceRecoverShiftTillEnd { + get { + return ResourceManager.GetString("MsgTraceRecoverShiftTillEnd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to RECOVERED. + /// + public static string MsgTraceRecoverSuccess { + get { + return ResourceManager.GetString("MsgTraceRecoverSuccess", resourceCulture); + } + } + } +} diff --git a/Irony/Resources.resx b/Irony/Resources.resx new file mode 100644 index 0000000..182aaef --- /dev/null +++ b/Irony/Resources.resx @@ -0,0 +1,457 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Nn + + + Yy + + + Argument list not found in the stack. Expected: ValueList, found: {0}. + + + Cannot convert value from type {0} to type {1}, type converter not defined. + + + Fatal error: + + + Construct '{0}' is not supported (yet) by language implementation. + + + Interpreter error, DataStack.Pop() operation failed - stack is empty. + + + Interpreter is busy. + + + Invalid AstMode value in call to Evaluate method. Node: {0}, mode: {1}. + + + Invalid character: '{0}'. + + + UnExprNode: no implementation for unary operator '{0}'. + + + Attempt to evaluate NULL AST node. The AST node for term '{0}' was not created during parsing. + + + Operator '{0}' is not defined for types {1} and {2}. + + + Operator '{0} not imlemented. + + + {0}: {1} + Location: ErrorMessage + + + Parsed tree is null, cannot evaluate. + + + Parse tree root is null, cannot evaluate. + + + Root AST node is null, cannot evaluate. + + + Root AST node does not implement IInterpretedAstNode interface, cannot evaluate. + + + Variable {0} is not a callable function. + + + Variable {0} not defined. + + + Invalid number of arguments. Expected {0}, found {1}. + + + ({0}:{1}) + + + Location: + + + None + + + Abort script(y/n)? + + + Exit console (y/n)? + + + Press any key to end the program. + + + Ambiguous grammar, unresolvable reduce-reduce conflicts. State {0}, lookaheads [{1}] + + + Ambiguous grammar, unresolvable shift-reduce conflicts. State {0}, lookaheads [{1}] + + + {0} State {1} on inputs: {2} + + + AstNodeType or AstNodeCreator is not set on non-terminals: {0}. Either set Term.AstConfig.NodeType, or provide default values in AstContext. + + + ParserDataBuilder error: inadequate state {0}, reduce item '{1}' has no lookaheads. + + + Non-terminal {0} has uninitialized Rule property. + + + Root property of the grammar is not set. + + + Reduce-reduce conflict. State {0}, lookaheads: {1}. Selected reduce on first production in conflict set. + + + Rule for NonTerminal {0} contains null as an operand in position {1} in one of productions. + + + Shift-reduce conflict. State {0}, lookaheads [{1}]. Selected shift as preferred action. + + + NLALR transform: Add WrapTail() in '.' position to [{0}]. + + + Syntax error, expected: {0} + + + [end-of-statement] + + + (unnamed) + + + {0} Console.\r\nPress Ctrl-C to exit the program.\r\n + + + Console + + + Failed to create AST node for non-terminal [{0}], error: {1} + + + Syntax error. + + + Unexpected end of file. + + + Unexpected indentation. + + + Unmatched closing brace '{0}'. + + + Accept + + + Operator, shift to {0}/reduce on {1}. + + + Reduce on {0} + + + Shift to {0} + + + (Unknown action type) + + + (INITIAL STATE) + + + Parsing conflict resolved in code. + + + Operator - resolved to {0} + + + Popped state from stack, pushing {0} + + + FAILED TO RECOVER + + + RECOVERING: popping stack, looking for state with error shift + + + RECOVERED + + + (EOF) + + + ... + + + "[{0}], at {1} + + + Invalid length of char literal - should be a single character. + + + Mal-formed string literal - cannot find termination symbol. + + + Invalid unicode escape ({0}), expected {1} hex digits. + + + Invalid \x escape, at least one digit expected. + + + Duplicate switch '{0}' for regular expression. + + + Cannot convert literal {0} to type {1}. + + + Invalid escape sequence: \{0}. + + + Invalid escape sequence. + + + Invalid escape symbol, expected 'u' or 'U' only. + + + Invalid number. + + + Invalid switch '{0}' for regular expression + + + Error in string literal [{0}]: No start/end symbols specified. + + + No end symbol for regex literal. + + + Number cannot be followed by a letter. + + + Unclosed comment block + + + (Key symbol) + + + (Keyword) + + + [line break] + + + Invalid dedent level, no previous matching indent found. + + + CodeOutlineFilter: line continuation symbol '{0}' should be added to Grammar.NonGrammarTerminals list. + + + RECOVERING: {0} + + + RECOVERING: Found state with shift on error : {0} + + + RECOVERING: Reached end of error production, reducing. + + + RECOVERING: Shifting Error term, {0} + + + RECOVERING: shifting until the end of error production. + + + Could not find a closing quote for quoted value. + + + Invalid arguments for IncDecNode AST node: either first or second argument should be '--' or '++'. + + + Invalid operation, attempt to assign to a constant or literal value. + + + Error in string literal [{0}]: IsTemplate flag is set, but TemplateSettings is not provided in AstNodeConfig property. + + + Duplicate start symbol {0} in string literal [{1}]. + + + Invalid embedded expression. + + + No ending tag '{0}' found in embedded expression. + + + Expression root non-terminal in template settings (AstNodeConfig property) in templated string literal [{0}] is not added to Roots set. Add it to SnippetRoots in grammar constructor. + + + Expression root is not specified in template settings (AstNodeConfig property) in templated string literal [{0}]. + + + ({0}) term passed as 'root' paramater to parserr is not Root or snippet root of the grammar. Add it to SnippetRoots set in grammar constructor. + + + ImpliedSymbolTerminal cannot be used in grammar with DisableScannerParserLink flag set + + + List non-terminals cannot be marked transient; list: ({0}) + + + Transient non-terminal must have zero or one non-punctuation child nodes; non-terminals: {0}. + + + The last term of production containing SyntaxError must be a terminal. NonTerminal: {0} + + + A terminal {0} has empty prefix. + + + No closing pair for opening symbol {0} + + + Operator '{0}' is not defined for types {1}. + + + Failed to find the ending tag '{0}' for a text literal. + + + Expected new line symbol. + + + Custom action did not execute: parser state or input did not change. + + + Executing custom action + + + Fatal error in parser: attempt to move back in the source. + + + Syntax error, unexpected input. + + \ No newline at end of file diff --git a/Irony/SilverlightOnly/Stopwatch.cs b/Irony/SilverlightOnly/Stopwatch.cs new file mode 100644 index 0000000..0bc00bc --- /dev/null +++ b/Irony/SilverlightOnly/Stopwatch.cs @@ -0,0 +1,19 @@ + +namespace System { +#if SILVERLIGHT + //Less precise version for Silverlight + internal class Stopwatch { + long _startTime, _endTime; + public void Start() { + _startTime = Environment.TickCount; + } + public void Stop() { + _endTime = Environment.TickCount; + } + public long ElapsedMilliseconds { + get {return _endTime - _startTime; } + } + } +#endif + +} diff --git a/Irony/Utilities/Extensions.cs b/Irony/Utilities/Extensions.cs new file mode 100644 index 0000000..84a6b60 --- /dev/null +++ b/Irony/Utilities/Extensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Irony.Parsing { + public static class ParsingEnumExtensions { + + public static bool IsSet(this TermFlags flags, TermFlags flag) { + return (flags & flag) != 0; + } + public static bool IsSet(this LanguageFlags flags, LanguageFlags flag) { + return (flags & flag) != 0; + } + public static bool IsSet(this ParseOptions options, ParseOptions option) { + return (options & option) != 0; + } + public static bool IsSet(this TermListOptions options, TermListOptions option) { + return (options & option) != 0; + } + public static bool IsSet(this ProductionFlags flags, ProductionFlags flag) { + return (flags & flag) != 0; + } + }//class + +} diff --git a/Irony/Utilities/LogMessage.cs b/Irony/Utilities/LogMessage.cs new file mode 100644 index 0000000..02ecf2f --- /dev/null +++ b/Irony/Utilities/LogMessage.cs @@ -0,0 +1,52 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +using Irony.Parsing; + +namespace Irony { + + public enum ErrorLevel { + Info = 0, + Warning = 1, + Error = 2, + } + + //Container for syntax errors and warnings + public class LogMessage { + public LogMessage(ErrorLevel level, SourceLocation location, string message, ParserState parserState) { + Level = level; + Location = location; + Message = message; + ParserState = parserState; + } + + public readonly ErrorLevel Level; + public readonly ParserState ParserState; + public readonly SourceLocation Location; + public readonly string Message; + + public override string ToString() { + return Message; + } + }//class + + public class LogMessageList : List { + public static int ByLocation(LogMessage x, LogMessage y) { + return SourceLocation.Compare(x.Location, y.Location); + } + } + +}//namespace diff --git a/Irony/Utilities/StringUtils.cs b/Irony/Utilities/StringUtils.cs new file mode 100644 index 0000000..3d0b760 --- /dev/null +++ b/Irony/Utilities/StringUtils.cs @@ -0,0 +1,100 @@ +#region License +/* ********************************************************************************** + * Copyright (c) Roman Ivantsov + * This source code is subject to terms and conditions of the MIT License + * for Irony. A copy of the license can be found in the License.txt file + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * MIT License. + * You must not remove this notice from this software. + * **********************************************************************************/ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Irony { + + public static class Strings { + public const string AllLatinLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + public const string DecimalDigits = "1234567890"; + public const string OctalDigits = "12345670"; + public const string HexDigits = "1234567890aAbBcCdDeEfF"; + public const string BinaryDigits = "01"; + + public static string JoinStrings(string separator, IEnumerable values) { + StringList list = new StringList(); + list.AddRange(values); + string[] arr = new string[list.Count]; + list.CopyTo(arr, 0); + return string.Join(separator, arr); + } + + }//class + + public class StringDictionary : Dictionary { } + public class CharList : List { } + + // CharHashSet: adding Hash to the name to avoid confusion with System.Runtime.Interoperability.CharSet + // Adding case sensitivity + public class CharHashSet : HashSet { + bool _caseSensitive; + public CharHashSet(bool caseSensitive = true) { + _caseSensitive = caseSensitive; + } + public new void Add(char ch) { + if (_caseSensitive) + base.Add(ch); + else { + base.Add(ch.ToString().ToLowerInvariant()[0]); + base.Add(ch.ToString().ToUpperInvariant()[0]); + } + + } + } + + public class TypeList : List { + public TypeList() { } + public TypeList(params Type[] types) : base(types) { } + } + + + public class StringSet : HashSet { + public StringSet() { } + public StringSet(StringComparer comparer) : base(comparer) { } + public override string ToString() { + return ToString(" "); + } + public void AddRange(params string[] items) { + base.UnionWith(items); + } + public string ToString(string separator) { + return Strings.JoinStrings(separator, this); + } + } + + public class StringList : List { + public StringList() { } + public StringList(params string[] args) { + AddRange(args); + } + public override string ToString() { + return ToString(" "); + } + public string ToString(string separator) { + return Strings.JoinStrings(separator, this); + } + //Used in sorting suffixes and prefixes; longer strings must come first in sort order + public static int LongerFirst(string x, string y) { + try {//in case any of them is null + if (x.Length > y.Length) return -1; + } catch { } + if (x == y) return 0; + return 1; + } + + }//class + + +} diff --git a/Irony/irony.snk b/Irony/irony.snk new file mode 100644 index 0000000000000000000000000000000000000000..54cbccafbe6b236237b6121accaa41c9eda2879e GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098esO)*A(5h&tiAndLr6d3G9>KEc8dCcb zt_nSqESh#h$)?C4mA>DlbX)I1O65dZQ#Uzx)$Ekk8-AppGzA9Gy&}Yv?X(2!dmUeDg;{ZAXVCq^bGgnKhxM=%MYN;LcV}dPdcpZ8(9F{m0a04BG#n; zy(R%=r6lz@1?t^Dte@T?rPhH(g9~F*dvL;oKMvdTjGW%2k}4sTm|(B=`_`2hmp?r} zViveL&d5e0a{oj)TVzA?vK-+$G&4&vDL#UXg0MmvBMF^xh9a^PFuv^ zi>_dWoz%L)9BD2`og(&jQGpLdMbYcY1~$E}A&DWPd_tBM*X4m}NC!&^H)aLxAKy_9 zdfxr-a|@4QN>OL?%{v~Jb^Z97bb^WS^WtJ43h6`0p**Q0igef<2jQnJ1H?P~ + + + + + \ No newline at end of file diff --git a/Languages/Refal/Ast/AuxiliaryNode.cs b/Languages/Refal/Ast/AuxiliaryNode.cs new file mode 100644 index 0000000..e794ac8 --- /dev/null +++ b/Languages/Refal/Ast/AuxiliaryNode.cs @@ -0,0 +1,68 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.Collections.Generic; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Ast; + +namespace Refal +{ + /// + /// Temporary AST nodes used internally while building AST. + /// + public class AuxiliaryNode : AstNode + { + public IList ChildParseNodes { get; private set; } + + public AuxiliaryNode() + { + ChildParseNodes = new List(); + } + + public override void Init(AstContext context, ParseTreeNode treeNode) + { + base.Init(context, treeNode); + + foreach (var node in treeNode.ChildNodes) + { + // linearize AuxiliaryNode children + if (node.AstNode is AuxiliaryNode) + { + var auxNode = node.AstNode as AuxiliaryNode; + + foreach (var n in auxNode.ChildNodes) + ChildNodes.Add(n); + + foreach (var n in auxNode.ChildParseNodes) + ChildParseNodes.Add(n); + + continue; + } + + // copy AstNode nodes + if (node.AstNode is AstNode) + { + ChildNodes.Add(node.AstNode as AstNode); + continue; + } + + // otherwise, save parse nodes + ChildParseNodes.Add(node); + } + } + + public override System.Collections.IEnumerable GetChildNodes() + { + throw new NotImplementedException("Auxiliary nodes should not appear in the final AST"); + } + + protected override object DoEvaluate(ScriptThread thread) + { + throw new NotImplementedException("Auxiliary node cannot be interpreted"); + } + } +} diff --git a/Languages/Refal/Ast/Block.cs b/Languages/Refal/Ast/Block.cs new file mode 100644 index 0000000..67f95cc --- /dev/null +++ b/Languages/Refal/Ast/Block.cs @@ -0,0 +1,82 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System.Collections.Generic; +using System.Linq; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Ast; + +namespace Refal +{ + /// + /// Block is a sequence of sentences. + /// + public class Block : AstNode + { + public IList Sentences { get; private set; } + + public Runtime.Pattern BlockPattern { get; set; } + + public Runtime.PassiveExpression InputExpression { get; set; } + + public Block() + { + Sentences = new List(); + } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + // copy sentences to block + if (node.AstNode is AuxiliaryNode) + { + var auxNode = node.AstNode as AuxiliaryNode; + + foreach (var s in auxNode.ChildNodes.OfType()) + { + s.Parent = this; + Sentences.Add(s); + } + } + } + + AsString = "block"; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + foreach (Sentence s in Sentences) + yield return s; + } + + protected override object DoEvaluate(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + foreach (var sentence in Sentences) + { + sentence.InputExpression = InputExpression; + sentence.BlockPattern = BlockPattern; + + var result = sentence.Evaluate(thread); + if (result != null) + { + // standard epilog + thread.CurrentNode = Parent; + return result; + } + } + + // standard Refal exception: input expression doesn't match any pattern + thread.ThrowScriptError("Recognition impossible"); + return null; + } + } +} diff --git a/Languages/Refal/Ast/Conditions.cs b/Languages/Refal/Ast/Conditions.cs new file mode 100644 index 0000000..a42b3e3 --- /dev/null +++ b/Languages/Refal/Ast/Conditions.cs @@ -0,0 +1,148 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.Linq; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Ast; + +namespace Refal +{ + /// + /// Where- and When-clauses. + /// + public class Conditions : AstNode + { + public Expression Expression { get; private set; } + + public Pattern Pattern { get; private set; } + + public Conditions MoreConditions { get; private set; } + + public Expression ResultExpression { get; private set; } + + public Block Block { get; private set; } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is Expression) + { + Expression = (node.AstNode as Expression); + } + else if (node.AstNode is AuxiliaryNode) + { + var nodes = (node.AstNode as AuxiliaryNode).ChildNodes; + Pattern = nodes.OfType().FirstOrDefault(); + Block = nodes.OfType().FirstOrDefault(); + MoreConditions = nodes.OfType().FirstOrDefault(); + ResultExpression = nodes.OfType().FirstOrDefault(); + } + + foreach (var astNode in new AstNode[] { Expression, Pattern, Block, MoreConditions, ResultExpression }) + { + if (astNode != null) + astNode.Parent = this; + } + } + + AsString = Block != null ? "with-clause" : "where-clause"; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + yield return Expression; + + if (Block != null) + yield return Block; + + if (Pattern != null) + yield return Pattern; + + if (ResultExpression != null) + yield return ResultExpression; + + if (MoreConditions != null) + yield return MoreConditions; + } + + protected override object DoEvaluate(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + // evaluate expression + var expression = Expression.EvaluateExpression(thread); + object result = null; + + // extract last recognized pattern (it contains bound variables) + var lastPattern = thread.GetLastPattern(); + if (lastPattern == null) + { + thread.ThrowScriptError("Internal error: last recognized pattern is lost."); + return null; // never gets here + } + + // with-clause + if (Block != null) + { + Block.InputExpression = expression; + Block.BlockPattern = lastPattern; + result = Block.Evaluate(thread); + } + + // where-clause + else if (Pattern != null) + { + result = EvaluateWhereClause(expression, lastPattern, thread); + } + + // internal compiler error + else + { + thread.ThrowScriptError("Internal error: AST node doen't represent neither where- nor when-clause."); + return null; // never get here + } + + // standard epilog + thread.CurrentNode = Parent; + return result; + } + + object EvaluateWhereClause(Runtime.PassiveExpression expr, Runtime.Pattern lastPattern, ScriptThread thread) + { + // instantiate where-clause pattern + var patt = Pattern.Instantiate(thread); + patt.CopyBoundVariables(lastPattern); + + // perform matching + var result = patt.Match(expr); + if (result) + { + // store last recognized pattern as a local variable + thread.SetLastPattern(patt); + + // match succeeded, return result expression + if (ResultExpression != null) + { + return ResultExpression.Evaluate(thread); + } + + // match succeeded, evaluate more conditions + if (MoreConditions != null) + { + return MoreConditions.Evaluate(thread); + } + } + + // matching failed, return nothing + return null; + } + } +} diff --git a/Languages/Refal/Ast/DefinedFunction.cs b/Languages/Refal/Ast/DefinedFunction.cs new file mode 100644 index 0000000..c7f543c --- /dev/null +++ b/Languages/Refal/Ast/DefinedFunction.cs @@ -0,0 +1,75 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Refal.Runtime; +using Irony.Ast; + +namespace Refal +{ + /// + /// DefinedFunction is a function defined in the current compulation unit. + /// + public class DefinedFunction : Function + { + public Block Block { get; private set; } + + public bool IsPublic { get; private set; } + + private ScopeInfo ScopeInfo { get; set; } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is IdentifierNode) + { + Name = (node.AstNode as IdentifierNode).Symbol; + } + else if (node.AstNode is Block) + { + Block = (node.AstNode as Block); + Block.Parent = this; + } + else if (node.Term is KeyTerm && node.Term.Name == "$ENTRY") + { + IsPublic = true; + } + } + + ScopeInfo = new ScopeInfo(this, context.Language.Grammar.CaseSensitive); + AsString = (IsPublic ? "public " : "private ") + Name; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + return Block.GetChildNodes(); + } + + public override object Call(ScriptThread thread, object[] parameters) + { + thread.PushScope(ScopeInfo, parameters); + + try + { + var expression = + parameters != null && parameters.Length > 0 ? + parameters[0] as PassiveExpression : null; + + Block.InputExpression = expression; + Block.BlockPattern = null; + + return Block.Evaluate(thread); + } + finally + { + thread.PopScope(); + } + } + } +} diff --git a/Languages/Refal/Ast/Expression.cs b/Languages/Refal/Ast/Expression.cs new file mode 100644 index 0000000..32c3ec7 --- /dev/null +++ b/Languages/Refal/Ast/Expression.cs @@ -0,0 +1,78 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System.Collections.Generic; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Refal.Runtime; +using Irony.Ast; + +namespace Refal +{ + /// + /// Expression is a sequence of symbols, macrodigits, bound variables and function calls. + /// + public class Expression : AstNode + { + public IList Terms { get; private set; } + + public Expression() + { + Terms = new List(); + } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is AstNode) + { + var astNode = node.AstNode as AstNode; + astNode.Parent = this; + Terms.Add(astNode); + } + } + + AsString = "expression"; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + foreach (var term in Terms) + yield return term; + } + + public bool IsEmpty + { + get { return Terms.Count == 0; } + } + + protected override object DoEvaluate(ScriptThread thread) + { + return EvaluateExpression(thread); + } + + internal Runtime.PassiveExpression EvaluateExpression(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + var terms = new List(); + + foreach (var term in Terms) + { + var result = term.Evaluate(thread); + terms.Add(result); + } + + // standard epilog + thread.CurrentNode = Parent; + + return PassiveExpression.Build(terms.ToArray()); + } + } +} diff --git a/Languages/Refal/Ast/ExpressionInBraces.cs b/Languages/Refal/Ast/ExpressionInBraces.cs new file mode 100644 index 0000000..5f28661 --- /dev/null +++ b/Languages/Refal/Ast/ExpressionInBraces.cs @@ -0,0 +1,57 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using Irony.Ast; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Refal.Runtime; + +namespace Refal +{ + /// + /// Expression or pattern in structure braces (). + /// + public class ExpressionInBraces : AstNode + { + public AstNode InnerExpression { get; private set; } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is AstNode) + { + var astNode = node.AstNode as AstNode; + astNode.Parent = this; + InnerExpression = astNode; + } + } + + AsString = "(structure braces)"; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + return InnerExpression.GetChildNodes(); + } + + protected override object DoEvaluate(ScriptThread thread) + { + return EvaluateExpression(thread); + } + + internal PassiveExpression EvaluateExpression(ScriptThread thread) + { + if (InnerExpression == null) + { + return PassiveExpression.Build(new OpeningBrace(), new ClosingBrace()); + } + + return PassiveExpression.Build(new OpeningBrace(), InnerExpression.Evaluate(thread), new ClosingBrace()); + } + } +} diff --git a/Languages/Refal/Ast/ExpressionVariable.cs b/Languages/Refal/Ast/ExpressionVariable.cs new file mode 100644 index 0000000..510d6c8 --- /dev/null +++ b/Languages/Refal/Ast/ExpressionVariable.cs @@ -0,0 +1,23 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +namespace Refal +{ + /// + /// Variable of form e.X that can be bound to any expression. + /// + public class ExpressionVariable : Variable + { + public override string Index + { + get { return base.Index; } + protected set { base.Index = "e." + value; } + } + + public override Runtime.Variable CreateVariable() + { + return new Runtime.ExpressionVariable(Index); + } + } +} diff --git a/Languages/Refal/Ast/ExternalFunction.cs b/Languages/Refal/Ast/ExternalFunction.cs new file mode 100644 index 0000000..4d7da40 --- /dev/null +++ b/Languages/Refal/Ast/ExternalFunction.cs @@ -0,0 +1,39 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using Irony.Ast; +using Irony.Interpreter; +using Irony.Parsing; + +namespace Refal +{ + /// + /// External function is a library function referenced from the current compilation unit. + /// External functions are not supported yet. + /// + public class ExternalFunction : Function + { + public void SetSpan(SourceSpan sourceSpan) + { + Span = sourceSpan; + } + + public override void Init(AstContext context, ParseTreeNode treeNode) + { + base.Init(context, treeNode); + AsString = "extern " + Name; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + yield break; + } + + public override object Call(ScriptThread thread, object[] parameters) + { + thread.ThrowScriptError("Calling external function is not supported"); + return null; + } + } +} diff --git a/Languages/Refal/Ast/Function.cs b/Languages/Refal/Ast/Function.cs new file mode 100644 index 0000000..cd84239 --- /dev/null +++ b/Languages/Refal/Ast/Function.cs @@ -0,0 +1,36 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using Irony.Interpreter; +using Irony.Interpreter.Ast; + +namespace Refal +{ + /// + /// Base node for all functions. + /// + public abstract class Function : AstNode, ICallTarget + { + public string Name { get; set; } // TODO: value.Replace("-", "__") + + protected override object DoEvaluate(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + // define function: bind function name to the current instance + var binding = thread.Bind(Name, BindingRequestFlags.Write | BindingRequestFlags.NewOnly); + binding.SetValueRef(thread, this); + + // set Evaluate method and return the current node + Evaluate = t => this; + + // standard epilog + thread.CurrentNode = Parent; + return this; + } + + public abstract object Call(ScriptThread thread, object[] parameters); + } +} diff --git a/Languages/Refal/Ast/FunctionCall.cs b/Languages/Refal/Ast/FunctionCall.cs new file mode 100644 index 0000000..32e0250 --- /dev/null +++ b/Languages/Refal/Ast/FunctionCall.cs @@ -0,0 +1,115 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.Linq; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Ast; + +namespace Refal +{ + /// + /// Function call. + /// + public class FunctionCall : AstNode + { + public string FunctionName { get; private set; } + + public Expression Expression { get; private set; } + + private SourceSpan? NameSpan { get; set; } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is AuxiliaryNode) + { + var auxNode = node.AstNode as AuxiliaryNode; + NameSpan = auxNode.Span; + + // function call can be either Identifier: + var identifier = auxNode.ChildNodes.OfType().FirstOrDefault(); + if (identifier != null) + { + FunctionName = identifier.Symbol; + continue; + } + + // or symbol of arithmetic operation: <- s.1 1> + var pnode = auxNode.ChildParseNodes.Where(n => n.Term != null).FirstOrDefault(); + if (pnode != null) + { + FunctionName = pnode.Term.Name; + continue; + } + + // and nothing else + throw new InvalidOperationException("Invalid function call"); + } + else if (node.AstNode is Expression) + { + var astNode = node.AstNode as Expression; + astNode.Parent = this; + Expression = astNode; + } + } + + // error anchor points to the exact error location in the source code + ErrorAnchor = (NameSpan != null ? NameSpan.Value : Span).Location; + AsString = "call " + FunctionName; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + return Expression.GetChildNodes(); + } + + private ICallTarget CallTarget { get; set; } + + protected override object DoEvaluate(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + var binding = thread.Bind(FunctionName, BindingRequestFlags.Read); + var result = binding.GetValueRef(thread); + if (result == null) + { + thread.ThrowScriptError("Unknown identifier: {0}", FunctionName); + return null; + } + + CallTarget = result as ICallTarget; + if (CallTarget == null) + { + thread.ThrowScriptError("This identifier cannot be called: {0}", FunctionName); + return null; + } + + // set Evaluate pointer + Evaluate = DoCall; + + // standard epilog is done by DoCall + return DoCall(thread); + } + + private object DoCall(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + var parameter = Expression.Evaluate(thread); + var result = CallTarget.Call(thread, new object[] { parameter }); + + // standard epilog + thread.CurrentNode = Parent; + return result; + } + } +} diff --git a/Languages/Refal/Ast/LiteralValueNodeHelper.cs b/Languages/Refal/Ast/LiteralValueNodeHelper.cs new file mode 100644 index 0000000..550bce1 --- /dev/null +++ b/Languages/Refal/Ast/LiteralValueNodeHelper.cs @@ -0,0 +1,46 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Ast; + +namespace Refal +{ + /// + /// Initializes Refal literal nodes. + /// + public static class LiteralValueNodeHelper + { + /// + /// Converts identifiers to compound symbols (strings in double quotes), + /// expands character strings (in single quotes) to arrays of characters + /// + public static void InitNode(AstContext context, ParseTreeNode parseNode) + { + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is LiteralValueNode) + { + if (node.Term.Name == "Char") + { + var literal = node.AstNode as LiteralValueNode; + literal.Value = literal.Value.ToString().ToCharArray(); + } + + parseNode.AstNode = node.AstNode; + } + else + { + // identifiers in expressions are treated as strings (True is same as "True") + parseNode.AstNode = new LiteralValueNode() + { + Value = node.FindTokenAndGetText(), + Span = node.Span + }; + } + } + } + } +} diff --git a/Languages/Refal/Ast/Pattern.cs b/Languages/Refal/Ast/Pattern.cs new file mode 100644 index 0000000..6a2f295 --- /dev/null +++ b/Languages/Refal/Ast/Pattern.cs @@ -0,0 +1,85 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System.Collections.Generic; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Ast; + +namespace Refal +{ + /// + /// Pattern is a passive expression that may contain free variables. + /// + public class Pattern : AstNode + { + public IList Terms { get; private set; } + + public bool IsEmpty + { + get { return Terms.Count == 0; } + } + + public Pattern() + { + Terms = new List(); + } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is AstNode) + { + var astNode = node.AstNode as AstNode; + astNode.Parent = this; + astNode.UseType = NodeUseType.Name; // do not evaluate pattern variables + Terms.Add(astNode); + } + } + + AsString = "pattern"; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + foreach (var term in Terms) + yield return term; + } + + protected override object DoEvaluate(ScriptThread thread) + { + return Instantiate(thread); + } + + private object[] EvaluateTerms(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + var terms = new List(); + + foreach (var term in Terms) + { + // in pattern, variables are never read + var result = term.Evaluate(thread); + terms.Add(result); + } + + // standard epilog + thread.CurrentNode = Parent; + + return terms.ToArray(); + } + + public Runtime.Pattern Instantiate(ScriptThread thread) + { + // evaluate pattern and instantiate Runtime.Pattern + return new Runtime.Pattern(EvaluateTerms(thread)); + } + } +} diff --git a/Languages/Refal/Ast/Program.cs b/Languages/Refal/Ast/Program.cs new file mode 100644 index 0000000..ecd6473 --- /dev/null +++ b/Languages/Refal/Ast/Program.cs @@ -0,0 +1,111 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System.Collections.Generic; +using System.Linq; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Refal.Runtime; +using Irony.Ast; + +namespace Refal +{ + /// + /// Program is a list of functions. + /// + public class Program : AstNode + { + public IDictionary Functions { get; private set; } + + public IList FunctionList { get; private set; } + + public Function EntryPoint { get; private set; } + + public Program() + { + Functions = new Dictionary(); + FunctionList = new List(); + EntryPoint = null; + } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is Function) + { + AddFunction(node.AstNode as Function); + } + else if (node.AstNode is AuxiliaryNode) + { + var ids = (node.AstNode as AuxiliaryNode).ChildNodes.OfType(); + + foreach (var id in ids) + { + ExternalFunction ef = new ExternalFunction(); + ef.SetSpan(id.Span); + ef.Name = id.Symbol; + AddFunction(ef); + } + } + } + + AsString = "Refal-5 program"; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + foreach (var fun in FunctionList) + yield return fun; + } + + public void AddFunction(Function function) + { + function.Parent = this; + Functions[function.Name] = function; + FunctionList.Add(function); + + if (function.Name == "Go") + { + EntryPoint = function; + } + } + + protected override object DoEvaluate(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + if (EntryPoint == null) + { + thread.ThrowScriptError("No entry point defined (entry point is a function named «Go»)"); + return null; + } + + // define built-in runtime library functions + var libraryFunctions = LibraryFunction.ExtractLibraryFunctions(thread, new RefalLibrary(thread)); + foreach (var libFun in libraryFunctions) + { + var binding = thread.Bind(libFun.Name, BindingRequestFlags.Write | BindingRequestFlags.ExistingOrNew); + binding.SetValueRef(thread, libFun); + } + + // define functions declared in the compiled program + foreach (var fun in FunctionList) + { + fun.Evaluate(thread); + } + + // call entry point with an empty expression + EntryPoint.Call(thread, new object[] { PassiveExpression.Build() }); + + // standard epilog + thread.CurrentNode = Parent; + return null; + } + } +} diff --git a/Languages/Refal/Ast/ScriptThreadExtensions.cs b/Languages/Refal/Ast/ScriptThreadExtensions.cs new file mode 100644 index 0000000..5ad9d76 --- /dev/null +++ b/Languages/Refal/Ast/ScriptThreadExtensions.cs @@ -0,0 +1,37 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.Collections.Generic; +using Irony.Interpreter; + +namespace Refal +{ + /// + /// Refal script thread should be able to store the last recognized pattern. + /// + public static class ScriptThreadExtensions + { + /// Unique names for implicit local variables + static string LastPatternSymbolName = Guid.NewGuid().ToString(); + + /// + /// Retrieve last evaluated pattern. + /// + public static Refal.Runtime.Pattern GetLastPattern(this ScriptThread thread) + { + var binding = thread.Bind(LastPatternSymbolName, BindingRequestFlags.Read); + return binding.GetValueRef(thread) as Refal.Runtime.Pattern; + } + + /// + /// Set last evaluated pattern. + /// + public static void SetLastPattern(this ScriptThread thread, Refal.Runtime.Pattern pattern) + { + var binding = thread.Bind(LastPatternSymbolName, BindingRequestFlags.Write | BindingRequestFlags.ExistingOrNew); + binding.SetValueRef(thread, pattern); + } + } +} diff --git a/Languages/Refal/Ast/Sentence.cs b/Languages/Refal/Ast/Sentence.cs new file mode 100644 index 0000000..bd79c23 --- /dev/null +++ b/Languages/Refal/Ast/Sentence.cs @@ -0,0 +1,111 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System.Linq; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Refal.Runtime; +using Irony.Ast; + +namespace Refal +{ + /// + /// Sentence is an element of a function. + /// There are two possible forms of sentences: + /// 1) pattern { conditions } = expression; + /// 2) pattern conditions block; + /// + public class Sentence : AstNode + { + public Pattern Pattern { get; private set; } + + public Conditions Conditions { get; private set; } + + public Expression Expression { get; private set; } + + public Runtime.Pattern BlockPattern { get; set; } + + public PassiveExpression InputExpression { get; set; } + + public override void Init(AstContext context, ParseTreeNode parseNode) + { + base.Init(context, parseNode); + + foreach (var node in parseNode.ChildNodes) + { + if (node.AstNode is Pattern) + { + Pattern = node.AstNode as Pattern; + } + else if (node.AstNode is AuxiliaryNode) + { + var nodes = (node.AstNode as AuxiliaryNode).ChildNodes; + Conditions = nodes.OfType().FirstOrDefault(); + Expression = nodes.OfType().FirstOrDefault(); + } + } + + foreach (var astNode in new AstNode[] { Pattern, Conditions, Expression }) + { + if (astNode != null) + { + astNode.Parent = this; + } + } + + AsString = "match"; + } + + public override System.Collections.IEnumerable GetChildNodes() + { + yield return Pattern; + + if (Conditions != null) + yield return Conditions; + + if (Expression != null) + yield return Expression; + } + + protected override object DoEvaluate(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + + // evaluate pattern and copy bound variables of the current block + var patt = Pattern.Instantiate(thread); + if (BlockPattern != null) + { + patt.CopyBoundVariables(BlockPattern); + } + + object result = null; + + // if pattern is recognized, calculate new expression and return true + var success = patt.Match(InputExpression); + if (success) + { + // store last recognized pattern as a local variable + thread.SetLastPattern(patt); + + // simple sentence + if (Expression != null) + { + result = Expression.Evaluate(thread); + } + + // sentence with a where- or when-clause + else if (Conditions != null) + { + result = Conditions.Evaluate(thread); + } + } + + // standard epilog + thread.CurrentNode = Parent; + return result; + } + } +} diff --git a/Languages/Refal/Ast/SymbolVariable.cs b/Languages/Refal/Ast/SymbolVariable.cs new file mode 100644 index 0000000..1b0ddb7 --- /dev/null +++ b/Languages/Refal/Ast/SymbolVariable.cs @@ -0,0 +1,23 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +namespace Refal +{ + /// + /// Variable of form s.X that can be bound to single symbol. + /// + public class SymbolVariable : Variable + { + public override string Index + { + get { return base.Index; } + protected set { base.Index = "s." + value; } + } + + public override Runtime.Variable CreateVariable() + { + return new Runtime.SymbolVariable(Index); + } + } +} diff --git a/Languages/Refal/Ast/TermVariable.cs b/Languages/Refal/Ast/TermVariable.cs new file mode 100644 index 0000000..58ed9da --- /dev/null +++ b/Languages/Refal/Ast/TermVariable.cs @@ -0,0 +1,23 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +namespace Refal +{ + /// + /// Variable of form t.X that can be bound either to a symbol or to expression in a structure braces. + /// + public class TermVariable : Variable + { + public override string Index + { + get { return base.Index; } + protected set { base.Index = "t." + value; } + } + + public override Runtime.Variable CreateVariable() + { + return new Runtime.TermVariable(Index); + } + } +} diff --git a/Languages/Refal/Ast/Variable.cs b/Languages/Refal/Ast/Variable.cs new file mode 100644 index 0000000..3ce2ee9 --- /dev/null +++ b/Languages/Refal/Ast/Variable.cs @@ -0,0 +1,106 @@ +// Refal5.NET interpreter +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using Irony.Interpreter; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Ast; + +namespace Refal +{ + /// + /// Variable is a part of refal expression that can be bound to a value. + /// Being part of a pattern is not bound to a value and is called "free variable". + /// In an expression to the right of "=" variable is bound to a value. + /// + public abstract class Variable : AstNode + { + public virtual string Index { get; protected set; } + + public static void CreateVariableNode(AstContext context, ParseTreeNode parseNode) + { + Variable varNode = null; + + foreach (ParseTreeNode nt in parseNode.ChildNodes) + { + // (e | s | t) + if (varNode == null) + { + switch (nt.Term.Name) + { + case "s": + varNode = new SymbolVariable(); + break; + + case "e": + varNode = new ExpressionVariable(); + break; + + case "t": + varNode = new TermVariable(); + break; + + default: + throw new ArgumentOutOfRangeException("Unknown variable type: " + nt.Term.Name); + } + continue; + } + + if (nt.Term.Name == ".") + continue; + + // Number | Identifier + if (nt.AstNode is LiteralValueNode) + { + varNode.Index = (nt.AstNode as LiteralValueNode).Value.ToString(); + } + else if (nt.AstNode is IdentifierNode) + { + varNode.Index = (nt.AstNode as IdentifierNode).Symbol; + } + } + + varNode.Span = parseNode.Span; + varNode.AsString = varNode.Index; + parseNode.AstNode = varNode; + } + + protected override object DoEvaluate(ScriptThread thread) + { + // standard prolog + thread.CurrentNode = this; + object result = null; + + // is this variable a part of a pattern? + if (UseType == NodeUseType.Name) + { + // don't evaluate it + result = CreateVariable(); + } + else + { + // get last recognized pattern + var pattern = thread.GetLastPattern(); + if (pattern == null) + { + thread.ThrowScriptError("No pattern recognized!"); + return null; + } + + // read variable from the last recognized pattern + result = pattern.GetVariable(Index); + } + + // standard epilog + thread.CurrentNode = Parent; + return result; + } + + /// + /// Create pattern variable + /// + public abstract Runtime.Variable CreateVariable(); + } +} diff --git a/Languages/Refal/Colorer/colorer.txt b/Languages/Refal/Colorer/colorer.txt new file mode 100644 index 0000000..10b23ae --- /dev/null +++ b/Languages/Refal/Colorer/colorer.txt @@ -0,0 +1,28 @@ + + Refal-5 scheme for Colorer-take5 + Written by Alexey Yakovlev + + License: MIT + + Installation + + 1. Copy refal.hrc to "%colorer%\hrc\more" directory + 2. Edit "%colorer%\hrc\proto.hrc", add + + + + /(.*\.ref)$/i + /\$ENTRY/ + + + to the '' section. + + Related links + + http://refal.com - Refal language home page + http://colorer.sourceforge.net - Colorer Library project + http://farmanager.com - Far Manager official site + http://plugring.farmanager.com - Far plugins + http://sharp-shooter.ru - My homepage + + Y [02-06-04], [07-07-11] diff --git a/Languages/Refal/Colorer/refal.hrc b/Languages/Refal/Colorer/refal.hrc new file mode 100644 index 0000000..cc5a579 --- /dev/null +++ b/Languages/Refal/Colorer/refal.hrc @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Languages/Refal/IronyAstBase.cd b/Languages/Refal/IronyAstBase.cd new file mode 100644 index 0000000..67b6271 --- /dev/null +++ b/Languages/Refal/IronyAstBase.cd @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Languages/Refal/IronyAstNodes.cd b/Languages/Refal/IronyAstNodes.cd new file mode 100644 index 0000000..9ef1a8d --- /dev/null +++ b/Languages/Refal/IronyAstNodes.cd @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Languages/Refal/Properties/AssemblyInfo.cs b/Languages/Refal/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..07b0645 --- /dev/null +++ b/Languages/Refal/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Refal5.NET")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Refal5.NET")] +[assembly: AssemblyCopyright("Copyright © Alexey Yakovlev 2006-2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("06abf268-6731-4151-a7aa-8415aab3cbdc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.*")] diff --git a/Languages/Refal/Refal.2010.csproj b/Languages/Refal/Refal.2010.csproj new file mode 100644 index 0000000..f556efb --- /dev/null +++ b/Languages/Refal/Refal.2010.csproj @@ -0,0 +1,99 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {19A86418-BA99-41F4-80A5-A7F3A6383123} + Library + Properties + Refal + Refal + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + + + \ No newline at end of file diff --git a/Languages/Refal/RefalAstNodes.cd b/Languages/Refal/RefalAstNodes.cd new file mode 100644 index 0000000..5693ed9 --- /dev/null +++ b/Languages/Refal/RefalAstNodes.cd @@ -0,0 +1,156 @@ + + + + + + AgAAAgAAAAAAAAAAAAAQAAAAAAAAAAAAAAACAIAQAAA= + Ast\Block.cs + + + + + + AAAEAgAAAAAAAAAAAAAAAAAQAAABAAAQAAACAIAAAIA= + Ast\Conditions.cs + + + + + + AAAEQAAAAAAAAAAAAAAAAAAAACAAAAAAAAACAIAAAAA= + Ast\DefinedFunction.cs + + + + + + AABACgAAAAAAAIAAAAAAAAAAAAAAAAAAAAACAIAAAAA= + Ast\Expression.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAABAAAA= + Ast\ExpressionVariable.cs + + + + + + AAAAQAAAAAAAAAAIAAAAAAAAAAAAAAAAAAACAIAAAAA= + Ast\ExternalFunction.cs + + + + + + + + + AAAAQgAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= + Ast\Function.cs + + + + + + + + + + AAAAAgAAAAAAAAAAAAACAAAAAAAAAAAQAAACAIAQAAA= + Ast\FunctionCall.cs + + + + + + AABACgAAAAAAAAAgAAQAAAAAAAAAAAAAAAACAIAAAAA= + Ast\Pattern.cs + + + + + + AAAAAgAQAAAAAAAAAAAAIAIAAAAAAAAAAAADAIAAAAA= + Ast\Program.cs + + + + + + AAAAAgAAAAAAAAAAABAQAAAQAAAAAAAQAAACAIAQAAA= + Ast\Sentence.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAABAAAA= + Ast\SymbolVariable.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAABAAAA= + Ast\TermVariable.cs + + + + + + + + + + + + + + + + + AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAIAAEAAABAAAA= + Ast\Variable.cs + + + + + + AAAAAgAAAABAAAAAAAAAAAAAAAAAAAAAAAACAIAAAAA= + Ast\AuxiliaryNode.cs + + + + + + + + + + + AAAAgAAACAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + Ast\ScriptThreadExtensions.cs + + + + + + AAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAA= + Ast\LiteralValueNodeHelper.cs + + + + + + + + + AAAAAgAAAAAAAIAAAAAAAAAAAABAAAAAAAACAIAAAAA= + Ast\ExpressionInBraces.cs + + + + \ No newline at end of file diff --git a/Languages/Refal/RefalGrammar.cs b/Languages/Refal/RefalGrammar.cs new file mode 100644 index 0000000..a2a842c --- /dev/null +++ b/Languages/Refal/RefalGrammar.cs @@ -0,0 +1,133 @@ +using System; +using System.Text; +using System.Collections.Generic; +using Irony.Interpreter.Ast; +using Irony.Parsing; +using Irony.Interpreter; + +namespace Refal +{ + /// + /// Refal-5 Grammar. + /// Written by Alexey Yakovlev, yallie@yandex.ru. + /// http://refal.codeplex.com + /// + [Language("Refal5", "0.2 alpha", "Refal-5.NET language interpreter")] + public class RefalGrammar : InterpretedLanguageGrammar + { + public RefalGrammar() : base(true) // case sensitive + { + GrammarComments = "Refal-5 language interpreter based on Irony toolkit."; + + // Terminals + var Number = new NumberLiteral("Number"); + Number.DefaultIntTypes = new TypeCode[] { TypeCode.Int32, TypeCode.Int64, NumberLiteral.TypeCodeBigInt }; + Number.AddPrefix("0x", NumberOptions.Hex); + Number.AddSuffix("u", TypeCode.UInt32); + Number.AddSuffix("l", TypeCode.Int64); + Number.AddSuffix("ul", TypeCode.UInt64); + Number.Options |= NumberOptions.IntOnly; + + var CharLiteral = new StringLiteral("Char", "'", StringOptions.AllowsAllEscapes); + var StringLiteral = new StringLiteral("String", "\"", StringOptions.AllowsAllEscapes); + var Identifier = new IdentifierTerminal("Identifier", "_-", ""); + + var LineComment = new CommentTerminal("LineComment", "*", "\n", "\r"); + LineComment.ValidateToken += LineComment_ValidateToken; + + var BlockComment = new CommentTerminal("BlockComment", "/*", "*/"); + NonGrammarTerminals.Add(LineComment); + NonGrammarTerminals.Add(BlockComment); + + // Non-terminals + var Program = new NonTerminal("Program", typeof(Program)); + var Definition = new NonTerminal("Definition"); + var Function = new NonTerminal("Function", typeof(DefinedFunction)); + var External = new NonTerminal("External", typeof(AuxiliaryNode)); + var IdentifierList = new NonTerminal("Identifier+", typeof(AuxiliaryNode)); + var Block = new NonTerminal("Block", typeof(Block)); + var Sentence = new NonTerminal("Sentence", typeof(Sentence)); + var RSentence = new NonTerminal("RSentence", typeof(AuxiliaryNode)); + var SentenceList = new NonTerminal("Sentence+", typeof(AuxiliaryNode)); + var Pattern = new NonTerminal("Pattern", typeof(Pattern)); + var PatternItem = new NonTerminal("PatternItem"); + var PatternInParentheses = new NonTerminal("(Pattern)", typeof(ExpressionInBraces)); + var Expression = new NonTerminal("Expression", typeof(Expression)); + var ExpressionItem = new NonTerminal("ExpressionItem"); + var ExpressionInParentheses = new NonTerminal("(Expression)", typeof(ExpressionInBraces)); + var Var = new NonTerminal("Variable", Variable.CreateVariableNode); + var VarPrefix = new NonTerminal("VariablePrefix"); + var VarIndex = new NonTerminal("VariableIndex"); + var Symbol = new NonTerminal("Symbol", LiteralValueNodeHelper.InitNode); + var Call = new NonTerminal("Call", typeof(FunctionCall)); + var FunctionName = new NonTerminal("FunctionName", typeof(AuxiliaryNode)); + var WhereOrWithClause = new NonTerminal("WhereOrWithClause", typeof(Conditions)); + var CommaOrAmpersand = new NonTerminal("CommaOrAmpersand"); + var RWhereOrWithClause = new NonTerminal("RWhereOrWithClause", typeof(AuxiliaryNode)); + var RExpressionOrWhereOrWithClause = new NonTerminal("RExpressionOrWhereOrWithClause"); + + var SemicolonOpt = new NonTerminal("[;]", Empty | ";"); + var EntryOpt = new NonTerminal("[ENTRY]", Empty | "$ENTRY"); + var Extern = new NonTerminal("Extern", ToTerm("$EXTRN") | "$EXTERN" | "$EXTERNAL"); + + // Rules + Root = Program; + + Program.Rule = MakePlusRule(Program, Definition); + Definition.Rule = Function | External; + External.Rule = Extern + IdentifierList + ";"; + IdentifierList.Rule = MakePlusRule(IdentifierList, ToTerm(","), Identifier); + + Function.Rule = EntryOpt + Identifier + Block + SemicolonOpt; + Block.Rule = "{" + SentenceList + SemicolonOpt + "}"; + SentenceList.Rule = MakePlusRule(SentenceList, ToTerm(";"), Sentence); + + Sentence.Rule = Pattern + RSentence; + RSentence.Rule = "=" + Expression | WhereOrWithClause; + Pattern.Rule = MakeStarRule(Pattern, PatternItem); + PatternItem.Rule = Var | Symbol | PatternInParentheses; + PatternInParentheses.Rule = "(" + Pattern + ")"; + Expression.Rule = MakeStarRule(Expression, ExpressionItem); + ExpressionItem.Rule = Call | Var | Symbol | ExpressionInParentheses; + ExpressionInParentheses.Rule = "(" + Expression + ")"; + + Var.Rule = VarPrefix + "." + VarIndex; + VarPrefix.Rule = ToTerm("e") | "s" | "t"; + VarIndex.Rule = Number | Identifier; + Symbol.Rule = StringLiteral | CharLiteral | Number | "True" | "False" | Identifier; + Call.Rule = "<" + FunctionName + Expression + ">"; + FunctionName.Rule = Identifier | "+" | "-" | "*" | "/"; + + WhereOrWithClause.Rule = CommaOrAmpersand + Expression + ":" + RWhereOrWithClause; + CommaOrAmpersand.Rule = ToTerm(",") | "&"; + RWhereOrWithClause.Rule = Block // with-clause + | Pattern + RExpressionOrWhereOrWithClause; // where-clause + RExpressionOrWhereOrWithClause.Rule = ToTerm("=") + Expression | WhereOrWithClause; + + // Punctuation, braces, transient terms, options + MarkPunctuation("(", ")"); + MarkPunctuation("{", "}"); + MarkPunctuation("<", ">"); + MarkPunctuation("=", ",", "&", ";"); + + RegisterBracePair("(", ")"); + RegisterBracePair("<", ">"); + RegisterBracePair("{", "}"); + + MarkTransient(Definition, PatternItem, ExpressionItem, SemicolonOpt, EntryOpt, Extern, CommaOrAmpersand, VarPrefix, VarIndex, RExpressionOrWhereOrWithClause); + LanguageFlags = LanguageFlags.CreateAst | LanguageFlags.SupportsBigInt | LanguageFlags.TailRecursive; + } + + void LineComment_ValidateToken(object sender, ParsingEventArgs args) + { + // if "*" is allowed in the current parser state, suppress comments starting with "*" + var parserState = args.Context.CurrentParserState; + if (parserState.ExpectedTerminals.Contains(ToTerm("*"))) + { + // rewind input stream and reject the token +// args.Context.SetSourceLocation(args.Context.CurrentToken.Location); + args.Context.CurrentToken = null; + } + } + } +} diff --git a/Languages/Refal/Runtime/FunctionNamesAttribute.cs b/Languages/Refal/Runtime/FunctionNamesAttribute.cs new file mode 100644 index 0000000..ac50437 --- /dev/null +++ b/Languages/Refal/Runtime/FunctionNamesAttribute.cs @@ -0,0 +1,25 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.Text; +using System.Linq; +using System.Collections.Generic; + +namespace Refal.Runtime +{ + /// + /// FunctionName attribute is used to specify Refal name(s) for run-time library method written in C#. + /// + [AttributeUsage(AttributeTargets.Method)] + class FunctionNamesAttribute : Attribute + { + public string[] Names { get; private set; } + + public FunctionNamesAttribute(params string[] names) + { + Names = names; + } + } +} diff --git a/Languages/Refal/Runtime/LibraryFunction.cs b/Languages/Refal/Runtime/LibraryFunction.cs new file mode 100644 index 0000000..8eb4023 --- /dev/null +++ b/Languages/Refal/Runtime/LibraryFunction.cs @@ -0,0 +1,86 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.IO; +using System.Text; +using System.Reflection; +using System.Collections.Generic; +using Irony.Interpreter.Ast; +using Irony.Interpreter; + +namespace Refal.Runtime +{ + using Irony.Parsing; + + /// + /// LibraryFunction is a function defined in the standard library and available to Refal program + /// + public class LibraryFunction : ICallTarget + { + public string Name { get; private set; } + + private LibraryDelegate Function { get; set; } + + delegate PassiveExpression LibraryDelegate(PassiveExpression value); + + private LibraryFunction(string n, LibraryDelegate fun) + { + Name = n; + Function = fun; + } + + public object Call(ScriptThread thread, object[] parameters) + { + var astNode = new AstNode(); // TODO: figure it out + var newScopeInfo = new ScopeInfo(astNode, thread.App.Language.Grammar.CaseSensitive); + thread.PushScope(newScopeInfo, parameters); + + try + { + var expression = + parameters != null && parameters.Length > 0 ? + parameters[0] as PassiveExpression : null; + + return Function(expression); + } + finally + { + thread.PopScope(); + } + } + + public static LibraryFunction[] ExtractLibraryFunctions(ScriptThread thread, object instance) + { + if (instance == null) + return new LibraryFunction[0]; + + var list = new List(); + var languageCaseSensitive = thread.App.Language.Grammar.CaseSensitive; + + MethodInfo[] methods = instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); + foreach (MethodInfo method in methods) + { + var fun = method.CreateDelegate(instance, false); + if (fun != null) + { + var fname = method.GetCustomAttribute(); + var names = (fname == null) ? new string[] { method.Name } : fname.Names; + foreach (var strName in names) + { + string name = languageCaseSensitive ? strName : strName.ToLowerInvariant(); + list.Add(new LibraryFunction(name, fun)); + } + } + } + + return list.ToArray(); + } + + public override string ToString() + { + return "refal function: " + Name; + } + } +} diff --git a/Languages/Refal/Runtime/PassiveExpression.cs b/Languages/Refal/Runtime/PassiveExpression.cs new file mode 100644 index 0000000..515c1a8 --- /dev/null +++ b/Languages/Refal/Runtime/PassiveExpression.cs @@ -0,0 +1,182 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.Text; + +namespace Refal.Runtime +{ + /// + /// Passive expression is expression that don't contain + /// execution braces: <> + /// + /// It is basically a collection of symbols (single characters, + /// strings - called 'compound characters' and treated as symbols, + /// macrodigits and identifiers, which can be thought of as a + /// special case of compound characters) + /// + public class PassiveExpression : System.Collections.CollectionBase + { + protected PassiveExpression() + { + } + + public static PassiveExpression Build(params object[] objects) + { + PassiveExpression result = new PassiveExpression(); + + // flatten expressions, if needed + foreach (object obj in objects) + { + if (obj is PassiveExpression) + { + foreach (object symbol in (PassiveExpression)obj) + result.Add(symbol); + } + else + result.Add(obj); + } + + return result; + } + + public object this[int index] + { + get { return List[index]; } + } + + public virtual int Add(object symbol) + { + if (symbol is char[]) + { + int index = -1; + + foreach (char c in (char[])symbol) + index = List.Add(c); + + return index; + } + else if (symbol != null) + return List.Add(symbol); + + return -1; + } + + public void Remove(object o) + { + List.Remove(o); + } + + public bool IsEmpty + { + get { return List.Count == 0; } + } + + public override int GetHashCode() + { + int hashCode = this.List.Count ^ (int)0xBAD1DEA; + + if (this.List.Count >= 1) + { + hashCode ^= List[0].GetHashCode(); + } + + return hashCode; + } + + public override bool Equals(object o) + { + bool equals = true; + + if (o is PassiveExpression) + { + PassiveExpression ex = (PassiveExpression)o; + + if (ex.Count != Count) + return false; + + for (int i = 0; i < Count; i++) + { + object my = this[i]; + object his = ex[i]; + + if (my == null) + { + if (his != null) + return false; + } + else // my != null + { + if (his == null) + return false; + + equals = my.Equals(his); + } + + if (!equals) + return false; + } + + return equals; + } + + // ex + return false; + } + + public override string ToString() + { + return ToStringBuilder(0).ToString(); + } + + public StringBuilder ToStringBuilder(int startIndex) + { + StringBuilder sb = new StringBuilder(); + + for (int i = startIndex; i < Count; i++) + { + object value = this[i]; + + if (value is PassiveExpression) + sb.AppendFormat("({0}) ", (value as PassiveExpression).ToStringBuilder(0).ToString()); + else if (value is char || value is StructureBrace) + sb.AppendFormat("{0}", value); + else + sb.AppendFormat("{0} ", value); + } + + return sb; + } + + public bool CompareToExpression(int startIndex, PassiveExpression expression) + { + if (expression == null || expression.IsEmpty) + return true; + + for (int i = 0; i < expression.Count; i++) + { + if (startIndex + i >= this.Count) + return false; + + object ex1 = this[startIndex + i]; + object ex2 = expression[i]; + + if (ex1 is OpeningBrace) + { + if (!(ex2 is OpeningBrace)) + return false; + } + else if (ex1 is ClosingBrace) + { + if (!(ex2 is ClosingBrace)) + return false; + } + else if (!ex1.Equals(ex2)) + return false; + } + + return true; + } + } +} diff --git a/Languages/Refal/Runtime/Pattern.cs b/Languages/Refal/Runtime/Pattern.cs new file mode 100644 index 0000000..25bd353 --- /dev/null +++ b/Languages/Refal/Runtime/Pattern.cs @@ -0,0 +1,182 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.Collections.Generic; + +namespace Refal.Runtime +{ + /// + /// Pattern is much like expression, but it can include free variables. + /// + public class Pattern : PassiveExpression + { + public IDictionary Variables { get; private set; } + + public Pattern(params object[] terms) + { + Variables = new Dictionary(); + + foreach (object term in terms) + Add(term); + } + + public new PatternItem this[int index] + { + get { return List[index] as PatternItem; } + } + + public override int Add(object item) + { + // handle variables in a special way + if (item is Variable) + { + // don't add duplicate variables, add same instances instead + Variable variable = (Variable)item; + if (Variables.ContainsKey(variable.Name)) + return base.Add(Variables[variable.Name]); + + // index variables by names + Variables[variable.Name] = variable; + int index = base.Add(variable); + variable.FirstOccurance = index; + return index; + } + + // don't wrap pattern item + if (item is PatternItem) + return base.Add(item); + + // decompose char[] into chars, wrap each char as symbol + if (item is char[]) + { + int index = -1; + foreach (char c in (char[])item) + index = List.Add(new Symbol(c)); + return index; + } + + // flatten PassiveExpressions + if (item is PassiveExpression) + { + var index = -1; + foreach (var exItem in (PassiveExpression)item) + { + index = Add(exItem); + } + return index; + } + + // wrap any other object as symbol pattern + return base.Add(new Symbol(item)); + } + + public object GetVariable(string name) + { + if (Variables[name] == null) + return null; + + return (Variables[name]).Value; + } + + public void CopyBoundVariables(Pattern pattern) + { + foreach (string name in pattern.Variables.Keys) + { + if (Variables.ContainsKey(name)) + { + Variable var = Variables[name]; + var.Value = pattern.GetVariable(name); + // first occurance of the variable is in another pattern + var.FirstOccurance = -1; + } + else + { + // copy bound variable from another pattern + Variable var = pattern.Variables[name]; + Variables[name] = var; + var.FirstOccurance = -1; + } + } + } + + public void ClearBoundValues(int startFromIndex) + { + foreach (string name in Variables.Keys) + { + Variable var = Variables[name]; + + if (var.FirstOccurance > startFromIndex) // not >=! + var.Value = null; + } + } + + public bool Match(PassiveExpression expression) + { + int exIndex = 0, patIndex = 0; + + while (patIndex < Count || exIndex < expression.Count) + { + if (patIndex >= Count) + { + if (!RollBackToLastPartialMatch(ref exIndex, ref patIndex)) + return false; + + continue; + } + + switch (this[patIndex].Match(expression, ref exIndex, patIndex++)) + { + case MatchResult.Success: + continue; + + case MatchResult.PartialSuccess: + SaveRollBackInfo(exIndex, patIndex - 1); + continue; + } + + if (!RollBackToLastPartialMatch(ref exIndex, ref patIndex)) + return false; + } + + return true; + } + + // stack holding positions of the last expression variables + Stack rollBackStack = null; + + struct RollbackInfo + { + public int exIndex; + public int patIndex; + } + + private void SaveRollBackInfo(int exIndex, int patIndex) + { + RollbackInfo info = new RollbackInfo(); + info.exIndex = exIndex; + info.patIndex = patIndex; + + if (rollBackStack == null) + rollBackStack = new Stack(); + + rollBackStack.Push(info); + } + + private bool RollBackToLastPartialMatch(ref int exIndex, ref int patIndex) + { + if (rollBackStack == null || rollBackStack.Count == 0) + return false; + + // restore state up to the last expression variable + var info = rollBackStack.Pop(); + exIndex = info.exIndex; + patIndex = info.patIndex; + + // clear values of all bound variables starting later than patIndex + ClearBoundValues(patIndex); + return true; + } + } +} diff --git a/Languages/Refal/Runtime/PatternItems.cs b/Languages/Refal/Runtime/PatternItems.cs new file mode 100644 index 0000000..8b1bf1e --- /dev/null +++ b/Languages/Refal/Runtime/PatternItems.cs @@ -0,0 +1,148 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; + +namespace Refal.Runtime +{ + /// + /// Pattern contains pattern items of three types: symbols, braces and variables. + /// Pattern items can match given expression + /// + public abstract class PatternItem + { + public PatternItem() + { + } + + // match expression at exIndex pointer, advance pointer as needed + public abstract MatchResult Match(PassiveExpression expression, ref int exIndex, int patIndex); + } + + /// + /// MatchResult represents result of matching operation + /// + public enum MatchResult + { + /// + /// Failure means than expression item don't match pattern item + /// + Failure, + + /// + /// Success means that item matches unambiguously + /// + Success, + + /// + /// PartialSuccess means that item matches ambiguously (can possibly find another match) + /// + PartialSuccess + } + + public class Symbol : PatternItem + { + object Value { get; set; } + + public Symbol(object value) + { + Value = value; + } + + public override MatchResult Match(PassiveExpression expression, ref int exIndex, int patIndex) + { + if (expression == null || exIndex >= expression.Count) + return MatchResult.Failure; + + // symbol matches single symbol + if ((Value == null && expression[exIndex] != null) || !Value.Equals(expression[exIndex])) + { + return MatchResult.Failure; + } + + exIndex++; + return MatchResult.Success; + } + + public override string ToString() + { + if (Value == null) + return "null"; + + if (Value is char) + return "'" + Value.ToString() + "'"; + + if (Value is string) + return "\"" + Value.ToString() + "\""; + + return Value.ToString(); + } + } + + public abstract class StructureBrace : PatternItem + { + public StructureBrace() + { + } + + public override MatchResult Match(PassiveExpression expression, ref int exIndex, int patIndex) + { + if (expression == null || exIndex >= expression.Count) + return MatchResult.Failure; + + // opening brace <=> opening brace, closing brace <=> closing brace + if (expression[exIndex].GetType() == GetType()) + { + exIndex++; + return MatchResult.Success; + } + + return MatchResult.Failure; + } + } + + public class OpeningBrace : StructureBrace + { + public OpeningBrace() + { + } + + public override int GetHashCode() + { + return '('; + } + + public override bool Equals(object obj) + { + return obj is OpeningBrace; + } + + public override string ToString() + { + return "("; + } + } + + public class ClosingBrace : StructureBrace + { + public ClosingBrace() + { + } + + public override int GetHashCode() + { + return ')'; + } + + public override bool Equals(object obj) + { + return obj is ClosingBrace; + } + + public override string ToString() + { + return ")"; + } + } +} diff --git a/Languages/Refal/Runtime/PatternVariables.cs b/Languages/Refal/Runtime/PatternVariables.cs new file mode 100644 index 0000000..24d771d --- /dev/null +++ b/Languages/Refal/Runtime/PatternVariables.cs @@ -0,0 +1,273 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; + +namespace Refal.Runtime +{ + /// + /// Pattern variables is a special kind of pattern items. + /// They can be bound to expressions. + /// + public abstract class Variable : PatternItem + { + public string Name { get; private set; } + + public int FirstOccurance { get; set; } + + public object Value { get; set; } + + public Variable(string name) + { + Name = name; + FirstOccurance = -1; + Value = null; + } + + public override MatchResult Match(PassiveExpression expression, ref int exIndex, int patIndex) + { + // if it's the first occurance of the variable, it can match any value + if (patIndex == FirstOccurance) + return MatchAny(expression, ref exIndex); + + // if it's not the first, it can match only the same value as before + return MatchSame(expression, ref exIndex); + } + + protected abstract MatchResult MatchAny(PassiveExpression expression, ref int exIndex); + + protected abstract MatchResult MatchSame(PassiveExpression expression, ref int exIndex); + + public override string ToString() + { + return Value == null ? Name : string.Format("{0}={1}", Name, Value); + } + } + + /// + /// SymbolVariable matches single symbol + /// + public class SymbolVariable : Variable + { + public SymbolVariable(string name) : base(name) + { + } + + public object Symbol + { + get { return base.Value; } + set { base.Value = value; } + } + + protected override MatchResult MatchAny(PassiveExpression expression, ref int exIndex) + { + if (expression == null || exIndex >= expression.Count) + return MatchResult.Failure; + + // match anything except braces + if (!(expression[exIndex] is StructureBrace)) + { + this.Symbol = expression[exIndex++]; + return MatchResult.Success; + } + + return MatchResult.Failure; + } + + protected override MatchResult MatchSame(PassiveExpression expression, ref int exIndex) + { + if (expression == null || exIndex >= expression.Count) + return MatchResult.Failure; + + // match the bound value + if (Value.Equals(expression[exIndex++])) + { + return MatchResult.Success; + } + + return MatchResult.Failure; + } + + public override string ToString() + { + return Value == null ? Name : string.Format("{0}={1}", Name, + Value is char ? "'" + Value.ToString() + "'" : + Value is string ? "\"" + Value.ToString() + "\"" : + Value.ToString()); + } + } + + /// + /// TermVariable matches either single symbol of expression in a structure braces + /// + public class TermVariable : Variable + { + // term is either a symbol or an expression in structure braces + public TermVariable(string name) : base(name) + { + } + + // return value if it's not an expression + public object Symbol + { + get { return (base.Value is PassiveExpression ? null : base.Value); } + set { base.Value = value; } + } + + // return value if it's passive expression + public PassiveExpression Expression + { + get { return base.Value as PassiveExpression; } + set { base.Value = value; } + } + + protected override MatchResult MatchAny(PassiveExpression expression, ref int exIndex) + { + if (expression == null || exIndex >= expression.Count) + return MatchResult.Failure; + + // match single symbol (symbol == not a brace) + if (!(expression[exIndex] is StructureBrace)) + { + this.Symbol = expression[exIndex++]; + return MatchResult.Success; + } + + // match subexpression + else if (expression[exIndex] is OpeningBrace) + { + this.Expression = PassiveExpression.Build(); + this.Expression.Add(expression[exIndex++]); + + // extract subexpression within the structure braces + int rank = 1; + while (exIndex < expression.Count && rank > 0) + { + object ex = expression[exIndex++]; + this.Expression.Add(ex); + + if (ex is OpeningBrace) + rank++; + else if (ex is ClosingBrace) + rank--; + } + + // subexpression with surrounding braces is extracted + if (rank == 0) + return MatchResult.Success; + } + + // unmatched braces + return MatchResult.Failure; + } + + protected override MatchResult MatchSame(PassiveExpression expression, ref int exIndex) + { + if (expression == null || exIndex >= expression.Count) + return MatchResult.Failure; + + // match same symbol + if (this.Symbol != null) + { + // match the bound value + if (Symbol.Equals(expression[exIndex++])) + return MatchResult.Success; + } + + // match same subexpression + else if (this.Expression != null) + { + if (expression.CompareToExpression(exIndex, this.Expression)) + { + exIndex += this.Expression.Count; + return MatchResult.Success; + } + } + + return MatchResult.Failure; + } + } + + /// + /// ExpressionVariable matches any expression + /// + public class ExpressionVariable : Variable + { + public ExpressionVariable(string name) : base(name) + { + } + + public PassiveExpression Expression + { + get { return base.Value as PassiveExpression; } + set { base.Value = value; } + } + + protected override MatchResult MatchAny(PassiveExpression expression, ref int exIndex) + { + if (this.Expression == null) + { + // start with empty expression, don't advance exIndex + this.Expression = PassiveExpression.Build(); + return MatchResult.PartialSuccess; + } + else + { + if (expression == null || exIndex >= expression.Count) + return MatchResult.Failure; + + // continue adding terms to expression + object ex = expression[exIndex++]; + + // add single symbol + if (!(ex is StructureBrace)) + { + this.Expression.Add(ex); + return MatchResult.PartialSuccess; + } + else if (ex is OpeningBrace) + { + // add subexpression + this.Expression.Add(ex); + + // extract subexpression within the structure braces + int rank = 1; + while (exIndex < expression.Count && rank > 0) + { + ex = expression[exIndex++]; + this.Expression.Add(ex); + + if (ex is OpeningBrace) + rank++; + else if (ex is ClosingBrace) + rank--; + } + + // subexpression with surrounding braces is extracted + if (rank == 0) + return MatchResult.PartialSuccess; + } + + return MatchResult.Failure; + } + } + + protected override MatchResult MatchSame(PassiveExpression expression, ref int exIndex) + { + if (expression != null && expression.CompareToExpression(exIndex, this.Expression)) + { + exIndex += this.Expression.Count; + return MatchResult.Success; + } + + return MatchResult.Failure; + } + + public override string ToString() + { + return Value == null ? Name : string.Format("{0}={1}", Name, + Value is PassiveExpression && (Value as PassiveExpression).Count > 0 ? Value.ToString() : "[]"); + } + } +} diff --git a/Languages/Refal/Runtime/RecognitionImpossible.cs b/Languages/Refal/Runtime/RecognitionImpossible.cs new file mode 100644 index 0000000..e6db724 --- /dev/null +++ b/Languages/Refal/Runtime/RecognitionImpossible.cs @@ -0,0 +1,28 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.IO; +using System.Text; +using Irony.Interpreter.Ast; +using Irony.Interpreter; +using System.Reflection; +using System.Collections.Generic; + +namespace Refal.Runtime +{ + /// + /// RecognitionImpossibleException means that the current function cannot be aplied to input expression. + /// + public class RecognitionImpossibleException : Exception + { + public RecognitionImpossibleException() : base() + { + } + + public RecognitionImpossibleException(string msg) : base(msg) + { + } + } +} diff --git a/Languages/Refal/Runtime/RefalLibrary.cs b/Languages/Refal/Runtime/RefalLibrary.cs new file mode 100644 index 0000000..903575d --- /dev/null +++ b/Languages/Refal/Runtime/RefalLibrary.cs @@ -0,0 +1,528 @@ +// Refal5.NET runtime +// Written by Alexey Yakovlev +// http://refal.codeplex.com + +using System; +using System.IO; +using System.Text; +using System.Reflection; +using System.Collections.Generic; +using Irony.Interpreter.Ast; +using Irony.Interpreter; +using System.Numerics; + +namespace Refal.Runtime +{ + /// + /// Refal run-time library. + /// + public class RefalLibrary + { + /// + /// Script execution thread. + /// + public ScriptThread ScriptThread { get; private set; } + + /// + /// File I/O support: handle (expression) -> StreamReader/StreamWriter. + /// + IDictionary OpenFiles { get; set; } + + /// + /// Bury/Dig functions expression storage. + /// + IDictionary BuriedKeys { get; set; } + + IDictionary BuriedValues { get; set; } + + /// + /// Command line arguments. + /// + protected string[] CommandLineArguments { get; set; } + + public RefalLibrary(ScriptThread thread) + { + ScriptThread = thread; + OpenFiles = new Dictionary(); + BuriedKeys = new Dictionary(); + BuriedValues = new Dictionary(); + CommandLineArguments = null; + } + + // Standard RTL routines + + public PassiveExpression Print(PassiveExpression expression) + { + if (expression == null) + return null; + + //Console.WriteLine("{0}", expression.ToStringBuilder(0)); + ScriptThread.App.WriteLine(expression.ToStringBuilder(0).ToString()); + + return expression; + } + + public PassiveExpression Prout(PassiveExpression expression) + { + if (expression == null) + return null; + + //Console.WriteLine("{0}", expression.ToStringBuilder(0)); + ScriptThread.App.WriteLine(expression.ToStringBuilder(0).ToString()); + + return null; + } + + public PassiveExpression Card(PassiveExpression expression) + { + throw new NotSupportedException(); + + /*string s = Console.ReadLine(); + + if (s != null) + return PassiveExpression.Build(s.ToCharArray()); + else + return PassiveExpression.Build(0);*/ + } + + public PassiveExpression Open(PassiveExpression expression) + { + // + if (expression == null || expression.Count < 1) + throw new ArgumentNullException("s.Mode"); + else if (expression.Count < 2) + throw new ArgumentNullException("s.D"); + + string mode = expression[0].ToString().ToUpper(); + string handle = expression[1].ToString(); + string fileName = string.Format("refal{0}.dat", handle); + + // fileName can be omitted + if (expression.Count > 2) + { + fileName = expression.ToStringBuilder(2).ToString(); + } + + // R - read, W - write, A - append + if (mode.StartsWith("R")) + { + OpenFiles[handle] = new StreamReader(File.OpenRead(fileName)); + } + else if (mode.StartsWith("W")) + { + OpenFiles[handle] = new StreamWriter(File.Create(fileName)); + } + else if (mode.StartsWith("A")) + { + OpenFiles[handle] = File.AppendText(fileName); + } + else + { + throw new NotSupportedException("Bad file open mode: " + mode + " (R, W, or A expected)"); + } + + // AFAIK, Open don't return anything + return null; + } + + public PassiveExpression Get(PassiveExpression expression) + { + if (expression == null || expression.IsEmpty) + return Card(expression); + + string handle = expression[0].ToString(); + StreamReader sr = OpenFiles[handle] as StreamReader; + + if (sr == null) + return Card(expression); + + string s = sr.ReadLine(); + if (s != null) + return PassiveExpression.Build(s.ToCharArray()); + else + return PassiveExpression.Build(0); + } + + public PassiveExpression Put(PassiveExpression expression) + { + if (expression == null || expression.IsEmpty) + return Prout(expression); + + string handle = expression[0].ToString(); + StreamWriter sw = OpenFiles[handle] as StreamWriter; + + if (sw == null) + return Prout(expression); + + sw.WriteLine("{0}", expression.ToStringBuilder(1)); + + PassiveExpression result = PassiveExpression.Build(expression); + result.Remove(result[0]); + return result; + } + + protected void CloseFiles() + { + foreach (object o in OpenFiles.Values) + { + if (o is StreamWriter) + (o as StreamWriter).Close(); + else if (o is StreamReader) + (o as StreamReader).Close(); + } + } + + public PassiveExpression Arg(PassiveExpression expression) + { + if (expression == null || expression.IsEmpty || CommandLineArguments == null) + return PassiveExpression.Build(); + + int index = Convert.ToInt32(expression[0]) - 1; // in Refal, index is 1-based + + if (index >= CommandLineArguments.Length) + { + return PassiveExpression.Build(); + } + + return PassiveExpression.Build(CommandLineArguments[index].ToCharArray()); + } + + public PassiveExpression Br(PassiveExpression expression) + { + //
: unexpected arguments"); + } + + public PassiveExpression Dg(PassiveExpression expression) + { + // + string strKey = expression.ToString(); + + if (BuriedValues.ContainsKey(strKey)) + { + PassiveExpression result = PassiveExpression.Build(BuriedValues[strKey] as PassiveExpression); + BuriedValues[strKey] = null; + BuriedKeys[strKey] = null; + return result; + } + + return PassiveExpression.Build(); + } + + public PassiveExpression Dgall(PassiveExpression expression) + { + List result = new List(); + foreach (string strKey in BuriedKeys.Keys) + { + result.AddRange(new object[] {new OpeningBrace(), BuriedKeys[strKey], '=', BuriedValues[strKey], new ClosingBrace()}); + BuriedKeys[strKey] = null; + BuriedValues[strKey] = null; + } + + return PassiveExpression.Build(result.ToArray()); + } + + // extract arguments specified as + void GetArguments(PassiveExpression expression, out object arg1, out object arg2) + { + var p = new Pattern(new TermVariable("t.1"), new ExpressionVariable("e.2")); + if (p.Match(expression)) + { + arg1 = p.GetVariable("t.1"); + arg2 = p.GetVariable("e.2"); + return; + } + + // can't find match + throw new RecognitionImpossibleException(); + } + + // find the first numeric symbol in an expression and convert to BigInteger + BigInteger ToBigInteger(object value) + { + // try convert expression + var expr = value as PassiveExpression; + if (expr != null) + { + int sign = 1; + + foreach (object o in expr) + { + if (o is StructureBrace) + continue; + + if (o is char) + { + var c = (char)o; + if (c == '-') + sign *= -1; + } + + if (o is int) + return new BigInteger(sign * (int)o); + + if (o is long) + return new BigInteger(sign * (long)o); + + if (o is BigInteger) + return sign * (BigInteger)o; + + // warning: BigInteger doesn't support parsing strings + if (o is string) + return new BigInteger(sign * Convert.ToInt64(o)); + } + + return BigInteger.Zero; + } + + // try convert single symbol + if (value is int) + return new BigInteger((int)value); + if (value is long) + return new BigInteger((long)value); + if (value is BigInteger) + return (BigInteger)value; + if (value is string) + return new BigInteger(Convert.ToInt64(value)); // TODO + return BigInteger.Zero; + } + + // extract arguments and convert to BigIntegers + void GetBigIntegerArguments(PassiveExpression expr, out BigInteger arg1, out BigInteger arg2) + { + object op1, op2; + GetArguments(expr, out op1, out op2); + + arg1 = ToBigInteger(op1); + arg2 = ToBigInteger(op2);; + } + + /// + /// Converts BigInteger to the minimal possible number type + /// Negative numbers get converted to positive numbers prefixed with '-' + /// For example, -520582(BigInteger) -> '-'(char) 52082(int) + /// + object[] ConvertBigIntegerToRefalNumber(BigInteger bigIntValue) + { + var negative = bigIntValue < 0; + var result = new object[negative ? 2 : 1]; + var valueIndex = negative ? 1 : 0; + if (negative) + { + result[0] = '-'; + bigIntValue = -bigIntValue; + } + + if (bigIntValue >= int.MinValue && bigIntValue <= int.MaxValue) + result[valueIndex] = (int)bigIntValue; + else if (bigIntValue >= long.MinValue && bigIntValue <= long.MaxValue) + result[valueIndex] = (long)bigIntValue; + else + result[valueIndex] = bigIntValue; + + return result; + } + + int ToInt32(object expression) + { + string s = expression.ToString().Trim(" \t\r\n()".ToCharArray()); + return Convert.ToInt32(s); + } + + [FunctionNames("+", "Add")] + public PassiveExpression Add(PassiveExpression expression) + { + BigInteger op1, op2; + GetBigIntegerArguments(expression, out op1, out op2); + + return PassiveExpression.Build(ConvertBigIntegerToRefalNumber(op1 + op2)); + } + + [FunctionNames("-", "Sub")] + public PassiveExpression Sub(PassiveExpression expression) + { + BigInteger op1, op2; + GetBigIntegerArguments(expression, out op1, out op2); + + return PassiveExpression.Build(ConvertBigIntegerToRefalNumber(op1 - op2)); + } + + [FunctionNames("*", "Mul")] + public PassiveExpression Mul(PassiveExpression expression) + { + BigInteger op1, op2; + GetBigIntegerArguments(expression, out op1, out op2); + + return PassiveExpression.Build(ConvertBigIntegerToRefalNumber(op1 * op2)); + } + + [FunctionNames("/", "Div")] + public PassiveExpression Div(PassiveExpression expression) + { + BigInteger op1, op2; + GetBigIntegerArguments(expression, out op1, out op2); + + return PassiveExpression.Build(ConvertBigIntegerToRefalNumber(op1 / op2)); + } + + public PassiveExpression Cp(PassiveExpression expression) + { + throw new NotImplementedException(); + } + + public PassiveExpression Rp(PassiveExpression expression) + { + throw new NotImplementedException(); + } + + public PassiveExpression Type(PassiveExpression expression) + { + if (expression == null || expression.IsEmpty) + return PassiveExpression.Build('*', 0, expression); + + object o = expression[0]; + + if (o is OpeningBrace) + return PassiveExpression.Build('B', 0, expression); + + if (o is char) + { + char c = (char)o; + char subtype = Char.IsUpper(c) ? 'u' : 'l'; + + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + return PassiveExpression.Build('L', subtype, expression); + + if (c >= '0' && c <= '9') + return PassiveExpression.Build('D', 0, expression); + + if (Char.IsControl(c)) + return PassiveExpression.Build('O', subtype, expression); + + // printable + return PassiveExpression.Build('P', subtype, expression); + } + + if (o is int || o is long) + return PassiveExpression.Build('N', 0, expression); + + if (o is string) + { + string s = o as string; + char subtype = 'i'; + + if (s.Length == 0 || !(Char.IsLetter(s[0])) || s.IndexOf(" ") >= 0) + subtype = 'q'; + + return PassiveExpression.Build('W', subtype, expression); + } + + return PassiveExpression.Build('P', 'l', expression); + } + + public PassiveExpression Mu(PassiveExpression expression) + { + throw new NotImplementedException(); + } + + [FunctionNames("Implode-Ext")] + public PassiveExpression Implode_Ext(PassiveExpression expression) + { + throw new NotImplementedException(); + } + + public PassiveExpression Implode(PassiveExpression expression) + { + string s = expression.ToString().Trim(); + + int index = 0; + while (index < s.Length && (char.IsLetterOrDigit(s[index]) || "-_".IndexOf(s[index]) >= 0)) + { + index++; + } + + return PassiveExpression.Build(s.Substring(0, index)); + } + + [FunctionNames("Explode", "Symb")] + public PassiveExpression Explode(PassiveExpression expression) + { + // convert expression to string and remove trailing space, if any + var sb = expression.ToStringBuilder(0); + if (sb.Length > 0 && sb[sb.Length - 1] == ' ') + sb.Length -= 1; + + return PassiveExpression.Build(sb.ToString().ToCharArray()); + } + + public PassiveExpression Numb(PassiveExpression expression) + { + if (expression == null || expression.IsEmpty) + return PassiveExpression.Build(); + + return PassiveExpression.Build(ConvertBigIntegerToRefalNumber(ToBigInteger(expression.ToString()))); + } + + public PassiveExpression Chr(PassiveExpression expression) + { + var args = new List(); + + foreach (object o in expression) + { + var v = (o is int) ? (char)Convert.ToByte(o) : o; + + // returns Environment.NewLine + if (o != null && o.ToString().ToLower() == "newline") + { + v = Environment.NewLine.ToCharArray(); + } + + args.Add(v); + } + + return PassiveExpression.Build(args.ToArray()); + } + + public PassiveExpression Ord(PassiveExpression expression) + { + var args = new List(); + + foreach (object o in expression) + { + var v = (o is char) ? Convert.ToInt32(o) : o; + args.Add(v); + } + + return PassiveExpression.Build(args.ToArray()); + } + + public PassiveExpression Divmod(PassiveExpression expression) + { + throw new NotImplementedException(); + } + + public PassiveExpression First(PassiveExpression expression) + { + throw new NotImplementedException(); + } + + public PassiveExpression Putout(PassiveExpression expression) + { + throw new NotImplementedException(); + } + } +} diff --git a/Languages/Refal/Runtime/ReflectionExtensions.cs b/Languages/Refal/Runtime/ReflectionExtensions.cs new file mode 100644 index 0000000..5278bec --- /dev/null +++ b/Languages/Refal/Runtime/ReflectionExtensions.cs @@ -0,0 +1,70 @@ +/* +// Exepack.NET helper class +// http://www.codeplex.com/exepack +// Copyright (c) 2008-2009 Alexey Yakovlev +*/ + +using System; +using System.Linq; +using System.Text; +using System.Collections.Generic; +using System.Reflection; + +namespace Refal.Runtime +{ + /// + /// Extension methods for System.Reflection classes + /// + public static class ReflectionExtensions + { + public static T GetCustomAttribute(this MemberInfo mi, bool inherit) where T : Attribute + { + object[] attributes = mi.GetCustomAttributes(typeof(T), inherit); + + if (attributes.Length == 0) + return null; + + return (T)attributes[0]; + } + + public static T[] GetCustomAttributes(this MemberInfo mi, bool inherit) where T : Attribute + { + object[] attributes = mi.GetCustomAttributes(typeof(T), inherit); + T[] result = new T[attributes.Length]; + Array.Copy(attributes, result, result.Length); + return result; + } + + public static T[] GetCustomAttributes(this MemberInfo mi) where T : Attribute + { + return mi.GetCustomAttributes(false); + } + + public static T GetCustomAttribute(this MemberInfo mi) where T : Attribute + { + return mi.GetCustomAttribute(false); + } + + // where T : Delegate — not supported by C# + + public static T CreateDelegate(this MethodInfo mi, object instance) where T : class // Delegate + { + return (T)(object)Delegate.CreateDelegate(typeof(T), instance, mi); + } + + public static T CreateDelegate(this MethodInfo mi) where T : class // Delegate + { + return (T)(object)Delegate.CreateDelegate(typeof(T), null, mi); + } + + public static T CreateDelegate(this MethodInfo mi, object instance, bool throwOnBindFailure) where T : class // Delegate + { + return (T)(object)Delegate.CreateDelegate(typeof(T), instance, mi, throwOnBindFailure); + } + + public static T CreateDelegate(this MethodInfo mi, bool throwOnBindFailure) where T : class // Delegate + { + return (T)(object)Delegate.CreateDelegate(typeof(T), null, mi, throwOnBindFailure); + } + } +} diff --git a/Languages/Refal/UnitTests/Properties/AssemblyInfo.cs b/Languages/Refal/UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a939c62 --- /dev/null +++ b/Languages/Refal/UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Refal5.NET Unit Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Refal5.NET Unit Tests")] +[assembly: AssemblyCopyright("Copyright © Alexey Yakovlev 2006-2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d441a891-c75e-4de5-997b-9d0fc31aaf9c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Languages/Refal/UnitTests/Refal.UnitTests.NUnit.2010.csproj b/Languages/Refal/UnitTests/Refal.UnitTests.NUnit.2010.csproj new file mode 100644 index 0000000..12b2932 --- /dev/null +++ b/Languages/Refal/UnitTests/Refal.UnitTests.NUnit.2010.csproj @@ -0,0 +1,145 @@ + + + + Debug + AnyCPU + + + 2.0 + {A34F4AE5-A6E5-43F9-9F16-A43F34599053} + Library + Properties + Refal.UnitTests + Refal.UnitTests.NUnit + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + full + false + bin\Debug\ + TRACE;DEBUG;NUNIT + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\..\..\..\Temp\NUnit-2.5.8.10295\bin\net-2.0\framework\nunit.framework.dll + + + + 3.5 + + + + + False + + + + + + + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + {19A86418-BA99-41F4-80A5-A7F3A6383123} + Refal.2010 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Languages/Refal/UnitTests/Refal.UnitTests.VsTest.2010.csproj b/Languages/Refal/UnitTests/Refal.UnitTests.VsTest.2010.csproj new file mode 100644 index 0000000..d61ebcd --- /dev/null +++ b/Languages/Refal/UnitTests/Refal.UnitTests.VsTest.2010.csproj @@ -0,0 +1,142 @@ + + + + Debug + AnyCPU + + + 2.0 + {94409071-FF65-4C3C-97EB-BD4FDEF90B4E} + Library + Properties + Refal.UnitTests + Refal.UnitTests.VsTest + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + + + False + + + + + + + + + + + {321A7F5D-00C2-4095-9970-075CDEE8C139} + 015.Irony.Interpreter.2010 + + + {D81F5C91-D7DB-46E5-BC99-49488FB6814C} + 010.Irony.2010 + + + {19A86418-BA99-41F4-80A5-A7F3A6383123} + Refal.2010 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Languages/Refal/UnitTests/RefalLibraryTests.cs b/Languages/Refal/UnitTests/RefalLibraryTests.cs new file mode 100644 index 0000000..8e74080 --- /dev/null +++ b/Languages/Refal/UnitTests/RefalLibraryTests.cs @@ -0,0 +1,403 @@ +using System; +using Refal.Runtime; + +namespace Refal.UnitTests +{ + #region Unit testing platform abstraction layer +#if NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestContext = System.Object; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + #endregion + + /// + /// Refal runtime library tests. + /// Written by Alexey Yakovlev, yallie@yandex.ru. + /// http://refal.codeplex.com + /// + [TestClass] + public class RefalLibraryTests + { + /// + /// Initialized by MSTest Framework + /// + public TestContext TestContext { get; set; } + + // int32, int32 -> int32 + + [TestMethod] + public void RefalLibrary_TestAddInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 123, new ClosingBrace(), 321); + var result = refal.Add(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 444); + } + + [TestMethod] + public void RefalLibrary_TestAddInt32Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 123, new ClosingBrace(), 321); + var result = refal.Add(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 198); + } + + [TestMethod] + public void RefalLibrary_TestSubInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321, new ClosingBrace(), 123); + var result = refal.Sub(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 198); + } + + [TestMethod] + public void RefalLibrary_TestSubInt32Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 123, new ClosingBrace(), 321); + var result = refal.Sub(expr); + + // result should be '-' 198 (two symbols) + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is int); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((int)result[1], 198); + } + + [TestMethod] + public void RefalLibrary_TestMulInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321, new ClosingBrace(), 123); + var result = refal.Mul(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 39483); + } + + [TestMethod] + public void RefalLibrary_TestMulInt32NegNeg() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 321, new ClosingBrace(), '-', 123); + var result = refal.Mul(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 39483); + } + + [TestMethod] + public void RefalLibrary_TestMulInt32PosNeg() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321, new ClosingBrace(), '-', 123); + var result = refal.Mul(expr); + + // result should be '-' 198 (two symbols) + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is int); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((int)result[1], 39483); + } + + [TestMethod] + public void RefalLibrary_TestMulInt32NegPos() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 321, new ClosingBrace(), 123); + var result = refal.Mul(expr); + + // result should be '-' 198 (two symbols) + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is int); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((int)result[1], 39483); + } + + [TestMethod] + public void RefalLibrary_TestDivInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321, new ClosingBrace(), 123); + var result = refal.Div(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 2); + } + + [TestMethod] + public void RefalLibrary_TestDivInt32PosNeg() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 321, new ClosingBrace(), 123); + var result = refal.Div(expr); + + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is int); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((int)result[1], 2); + } + + [TestMethod] + public void RefalLibrary_TestDivInt32NegPos() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321, new ClosingBrace(), '-', 123); + var result = refal.Div(expr); + + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is int); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((int)result[1], 2); + } + + [TestMethod] + public void RefalLibrary_TestDivInt32NegNeg() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 321, new ClosingBrace(), '-', 123); + var result = refal.Div(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 2); + } + + // int64, int64 -> int32 + + [TestMethod] + public void RefalLibrary_TestAddInt64GivingInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 123L, new ClosingBrace(), 321L); + var result = refal.Add(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 444); + } + + [TestMethod] + public void RefalLibrary_TestAddInt64NegativeGivingInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 123L, new ClosingBrace(), 321L); + var result = refal.Add(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 198); + } + + [TestMethod] + public void RefalLibrary_TestSubInt64GivingInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321L, new ClosingBrace(), 123L); + var result = refal.Sub(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 198); + } + + [TestMethod] + public void RefalLibrary_TestSubInt64GivingInt32Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 123L, new ClosingBrace(), 321L); + var result = refal.Sub(expr); + + // result should be '-' 198 (two symbols) + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is int); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((int)result[1], 198); + } + + [TestMethod] + public void RefalLibrary_TestMulInt64GivingInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321L, new ClosingBrace(), 123L); + var result = refal.Mul(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 39483); + } + + [TestMethod] + public void RefalLibrary_TestMulInt64GivingInt32Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321L, new ClosingBrace(), '-', 123L); + var result = refal.Mul(expr); + + // result should be '-' 198 (two symbols) + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is int); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((int)result[1], 39483); + } + + [TestMethod] + public void RefalLibrary_TestDivInt64GivingInt32() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321L, new ClosingBrace(), 123L); + var result = refal.Div(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 2); + } + + [TestMethod] + public void RefalLibrary_TestDivInt64GivingInt32Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 321L, new ClosingBrace(), '-', 123L); + var result = refal.Div(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is int); + Assert.AreEqual((int)result[0], 2); + } + + // int64, int64 -> int64 + + [TestMethod] + public void RefalLibrary_TestAddInt64() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 123123123123, new ClosingBrace(), 321321321321); + var result = refal.Add(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is long); + Assert.AreEqual((long)result[0], 444444444444); + } + + [TestMethod] + public void RefalLibrary_TestAddInt64Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 123123123123, new ClosingBrace(), 321321321321); + var result = refal.Add(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is long); + Assert.AreEqual((long)result[0], 198198198198); + } + + [TestMethod] + public void RefalLibrary_TestSubInt64() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321321321321, new ClosingBrace(), 123123123123L); + var result = refal.Sub(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is long); + Assert.AreEqual((long)result[0], 198198198198); + } + + [TestMethod] + public void RefalLibrary_TestSubInt64Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 123123123123, new ClosingBrace(), 321321321321); + var result = refal.Sub(expr); + + // result should be '-' 444444444444 (two symbols) + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is long); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((long)result[1], 444444444444); + } + + [TestMethod] + public void RefalLibrary_TestMulInt64() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321321L, new ClosingBrace(), 123123L); + var result = refal.Mul(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is long); + Assert.AreEqual((long)result[0], 39562005483); + } + + [TestMethod] + public void RefalLibrary_TestMulInt64Negative() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), '-', 321321L, new ClosingBrace(), '-', 123123L); + var result = refal.Mul(expr); + + Assert.AreEqual(result.Count, 1); + Assert.IsTrue(result[0] is long); + Assert.AreEqual((long)result[0], 39562005483); + } + + [TestMethod] + public void RefalLibrary_TestDivInt64() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(new OpeningBrace(), 321321321321321, new ClosingBrace(), '-', 123L); + var result = refal.Div(expr); + + // result should be '-' 2612368466027 (two symbols) + Assert.AreEqual(result.Count, 2); + Assert.IsTrue(result[0] is char); + Assert.IsTrue(result[1] is long); + Assert.AreEqual((char)result[0], '-'); + Assert.AreEqual((long)result[1], 2612368466027); + } + + [TestMethod] + public void RefalLibrary_TestChr() + { + var refal = new RefalLibrary(null); + var expr = PassiveExpression.Build(13, 10, 0x20, "NewLine"); + var result = refal.Chr(expr); + + // result should be '\r', '\n', ' ' and Environment.NewLine + var expected = ("\r\n " + Environment.NewLine).ToCharArray(); + Assert.AreEqual(result.Count, expected.Length); + + for (int i = 0; i < result.Count; i++) + { + Assert.IsTrue(result[i] is char); + Assert.AreEqual(expected[i], (char)result[i]); + } + } + } +} diff --git a/Languages/Refal/UnitTests/RefalPatternMatchingTests.cs b/Languages/Refal/UnitTests/RefalPatternMatchingTests.cs new file mode 100644 index 0000000..1202e6f --- /dev/null +++ b/Languages/Refal/UnitTests/RefalPatternMatchingTests.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Refal.Runtime; + +namespace Refal.Runtime.UnitTests +{ + #region Unit testing platform abstraction layer +#if NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestContext = System.Object; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + #endregion + + /// + /// Refal pattern matching tests. + /// Written by Alexey Yakovlev, yallie@yandex.ru. + /// http://refal.codeplex.com + /// + [TestClass] + public class RefalPatternMatchingTests + { + // elementary tests + + [TestMethod] + public void Symbol_MatchesSameSymbol() + { + var pattern = new Pattern(123); // 123 + var expr = PassiveExpression.Build(123); // 123 + + var result = pattern.Match(expr); + Assert.IsTrue(result); + } + + [TestMethod] + public void Symbol_DontMatchAnotherSymbol() + { + var pattern = new Pattern(123); // 123 + var expr = PassiveExpression.Build('X'); // 'X' + + var result = pattern.Match(expr); + Assert.IsFalse(result); + } + + [TestMethod] + public void SymbolChain_MatchesSameSymbolChain() + { + var pattern = new Pattern(123, 'T', 'e', 's', 't'); // 123 'Test' + var expr = PassiveExpression.Build(123, 'T', 'e', 's', 't'); // 123 'Test' + + var result = pattern.Match(expr); + Assert.IsTrue(result); + } + + [TestMethod] + public void SymbolChain_DontMatchDifferentSymbolChain() + { + var pattern = new Pattern(123, 'T', 'e', 's', 't'); // 123 'Test' + var expr = PassiveExpression.Build(123, 'T', 'E', 'S', 'T'); // 123 'TEST' + + var result = pattern.Match(expr); + Assert.IsFalse(result); + } + + [TestMethod] + public void SymbolVariable_MatchesAnySymbol() + { + var pattern = new Pattern(new SymbolVariable("s.1")); // s.1 + var expr = PassiveExpression.Build(123); // 123 + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("s.1")); + Assert.AreEqual(123, pattern.Variables["s.1"].Value); + + expr = PassiveExpression.Build('A'); // 'A' + + result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("s.1")); + Assert.AreEqual('A', pattern.Variables["s.1"].Value); + } + + [TestMethod] + public void SymbolVariable_DontMatchSymbolChain() + { + var pattern = new Pattern(new SymbolVariable("s.1")); // s.1 + var expr = PassiveExpression.Build(123, 321); // 123 321 + + var result = pattern.Match(expr); + Assert.IsFalse(result); + } + + [TestMethod] + public void SymbolVariable_DontMatchBraces() + { + var pattern = new Pattern(new SymbolVariable("s.1")); // s.1 + var expr = PassiveExpression.Build(new OpeningBrace()); // ( + + var result = pattern.Match(expr); + Assert.IsFalse(result); + } + + [TestMethod] + public void TermVariable_MatchesAnySymbol() + { + var pattern = new Pattern(new TermVariable("t.1")); // t.1 + var expr = PassiveExpression.Build(123); // 123 + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("t.1")); + Assert.AreEqual(123, pattern.Variables["t.1"].Value); + + expr = PassiveExpression.Build('A'); // 'A' + + result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("t.1")); + Assert.AreEqual('A', pattern.Variables["t.1"].Value); + } + + [TestMethod] + public void TermVariable_DontMatchSymbolChain() + { + var pattern = new Pattern(new TermVariable("t.1")); // t.1 + var expr = PassiveExpression.Build(123, 321); // 123 321 + + var result = pattern.Match(expr); + Assert.IsFalse(result); + } + + [TestMethod] + public void TermVariable_MatchesEmptyExpressionInBraces() + { + var pattern = new Pattern(new TermVariable("t.1")); // t.1 + var expr = PassiveExpression.Build(new OpeningBrace(), new ClosingBrace()); // () + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("t.1")); + Assert.AreEqual(PassiveExpression.Build(new OpeningBrace(), new ClosingBrace()), pattern.Variables["t.1"].Value); + } + + [TestMethod] + public void TermVariable_MatchesNonEmptyExpressionInBraces() + { + var pattern = new Pattern(new TermVariable("t.1")); // t.1 + var expr = PassiveExpression.Build(new OpeningBrace(), "Hello", new ClosingBrace()); // () + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("t.1")); + Assert.AreEqual(PassiveExpression.Build(new OpeningBrace(), "Hello", new ClosingBrace()), pattern.Variables["t.1"].Value); + } + + [TestMethod] + public void ExpressionVariable_MatchesEmptyExpression() + { + var pattern = new Pattern(new ExpressionVariable("e.1")); // e.1 + var expr = PassiveExpression.Build(); // + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("e.1")); + } + + [TestMethod] + public void ExpressionVariableInBraces_DontMatchEmptyExpression() + { + var pattern = new Pattern(new OpeningBrace(), new ExpressionVariable("e.1"), new ClosingBrace()); // (e.1) + var expr = PassiveExpression.Build(); // + + var result = pattern.Match(expr); + Assert.IsFalse(result); + } + + [TestMethod] + public void ExpressionVariableInBraces_MatchesEmptyBraces() + { + var pattern = new Pattern(new OpeningBrace(), new ExpressionVariable("e.1"), new ClosingBrace()); // (e.1) + var expr = PassiveExpression.Build(new OpeningBrace(), new ClosingBrace()); // () + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("e.1")); + } + + [TestMethod] + public void ExpressionVariableInBraces_MatchesNonEmptyBraces() + { + var pattern = new Pattern(new OpeningBrace(), new ExpressionVariable("e.1"), new ClosingBrace()); // (e.1) + var expr = PassiveExpression.Build(new OpeningBrace(), 123, new ClosingBrace()); // (123) + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("e.1")); + Assert.AreEqual(PassiveExpression.Build(123), pattern.Variables["e.1"].Value); + } + + // non-elementary tests + + [TestMethod] + public void ComplexPattern_MatchesExpression1() + { + var pattern = new Pattern(new OpeningBrace(), new ExpressionVariable("e.1"), '0', new ClosingBrace(), + new OpeningBrace(), new ExpressionVariable("e.2"), new SymbolVariable("s.3"), new ClosingBrace()); // (e.1 '0') (e.2 s.3) + var expr = PassiveExpression.Build(new OpeningBrace(), '0', new ClosingBrace(), + new OpeningBrace(), '0', new ClosingBrace()); // ('0') ('0') + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("e.1")); + Assert.IsTrue(pattern.Variables.ContainsKey("e.2")); + Assert.IsTrue(pattern.Variables.ContainsKey("s.3")); + Assert.AreEqual(PassiveExpression.Build(), pattern.Variables["e.1"].Value); + Assert.AreEqual(PassiveExpression.Build(), pattern.Variables["e.2"].Value); + Assert.AreEqual('0', pattern.Variables["s.3"].Value); + } + + [TestMethod] + public void ComplexPattern_MatchesExpression2() + { + var pattern = new Pattern(new OpeningBrace(), new ExpressionVariable("e.1"), new SymbolVariable("s.3"), + new ClosingBrace(), new OpeningBrace(), new ExpressionVariable("e.2"), '0', new ClosingBrace()); // (e.1 s.3) (e.2 '0') + var expr = PassiveExpression.Build(new OpeningBrace(), '0', '1', new ClosingBrace(), + new OpeningBrace(), '0', new ClosingBrace()); // ('01') ('0') + + var result = pattern.Match(expr); + Assert.IsTrue(result); + Assert.IsTrue(pattern.Variables.ContainsKey("e.1")); + Assert.IsTrue(pattern.Variables.ContainsKey("e.2")); + Assert.IsTrue(pattern.Variables.ContainsKey("s.3")); + Assert.AreEqual(PassiveExpression.Build('0'), pattern.Variables["e.1"].Value); + Assert.AreEqual(PassiveExpression.Build(), pattern.Variables["e.2"].Value); + Assert.AreEqual('1', pattern.Variables["s.3"].Value); + } + } +} diff --git a/Languages/Refal/UnitTests/RefalRegressionTests.cs b/Languages/Refal/UnitTests/RefalRegressionTests.cs new file mode 100644 index 0000000..701eee5 --- /dev/null +++ b/Languages/Refal/UnitTests/RefalRegressionTests.cs @@ -0,0 +1,188 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; +using Irony; +using Irony.Parsing; + +namespace Refal.UnitTests +{ + #region Unit testing platform abstraction layer +#if NUNIT + using NUnit.Framework; + using TestClass = NUnit.Framework.TestFixtureAttribute; + using TestMethod = NUnit.Framework.TestAttribute; + using TestContext = System.Object; +#else + using Microsoft.VisualStudio.TestTools.UnitTesting; +#endif + #endregion + + /// + /// Refal regression tests. + /// Written by Alexey Yakovlev, yallie@yandex.ru. + /// http://refal.codeplex.com + /// + [TestClass] + public class RefalRegressionTests + { + /// + /// Resource name = DefaultNamespace + "." + FolderName + "." + FileName + /// Example: Irony.Samples.Refal.Samples.hello.ref + /// + const string BaseResourceName = "Refal.UnitTests.Sources."; + + /// + /// Initialized by MSTest Framework + /// + public TestContext TestContext { get; set; } + + [TestMethod] + public void RefalTest_Hello() + { + RunSampleAndCompareResults("hello.ref", "hello.txt"); + } + + [TestMethod] + public void RefalTest_Binary() + { + RunSampleAndCompareResults("binary.ref", "binary.txt"); + } + + [TestMethod] + public void RefalTest_Factorial() + { + RunSampleAndCompareResults("factorial.ref", "factorial.txt"); + } + + [TestMethod] + public void RefalTest_ChangeV1() + { + RunSampleAndCompareResults("change-v1.ref", "change.txt"); + } + + [TestMethod] + public void RefalTest_ChangeV2() + { + RunSampleAndCompareResults("change-v2.ref", "change.txt"); + } + + [TestMethod] + public void RefalTest_Italian() + { + RunSampleAndCompareResults("italian.ref", "italian.txt"); + } + + [TestMethod] + public void RefalTest_Palyndrome() + { + RunSampleAndCompareResults("palyndrome.ref", "palyndrome.txt"); + } + + [TestMethod] + public void RefalTest_ArithmeticTranslator() + { + RunSampleAndCompareResults("arith.ref", "arith.txt"); + } + + [TestMethod] + public void RefalTest_PrettyPrintExpressions() + { + RunSampleAndCompareResults("pretty.ref", "pretty.txt"); + } + + [TestMethod] + public void RefalTest_QuinePlain() + { + RunSampleAndCompareResults("quine-plain.ref"); + } + + [TestMethod] + public void RefalTest_QuineSimple() + { + RunSampleAndCompareResults("quine-simple.ref"); + } + + [TestMethod] + public void RefalTest_QuineXplained() + { + RunSampleAndCompareResults("quine-xplained.ref"); + } + + [TestMethod] + public void RefalTest_XtrasBigint() + { + RunSampleAndCompareResults("xtras-bigint.ref", "xtras-bigint.txt"); + } + + [TestMethod] + public void RefalTest_XtrasFactorial() + { + RunSampleAndCompareResults("xtras-factorial.ref", "xtras-factorial.txt"); + } + + [TestMethod] + public void RefalTest_99BottlesV1() + { + RunSampleAndCompareResults("99-bottles-v1.ref", "99-bottles-v1.txt"); + } + + [TestMethod] + public void RefalTest_99BottlesV2() + { + RunSampleAndCompareResults("99-bottles-v2.ref", "99-bottles-v2.txt"); + } + + [TestMethod] + public void RefalTest_BrainfuckInterpreter() + { + RunSampleAndCompareResults("brainfuck.ref", "brainfuck.txt"); + } + + /// + /// Load sample program from resources, run it and check its output + /// + void RunSampleAndCompareResults(string programResourceName, string outputResourceName) + { + var grammar = new RefalGrammar(); + var parser = new Parser(grammar); + var parseTree = parser.Parse(LoadResourceText(programResourceName)); + + Assert.IsNotNull(parseTree); + Assert.IsFalse(parseTree.HasErrors()); + + string result = grammar.RunSample(new RunSampleArgs(parser.Language, null, parseTree)); + Assert.IsNotNull(result); + Assert.AreEqual(LoadResourceText(outputResourceName), result); + } + + /// + /// Load quine program from resources, run it and compare its output with itself + /// + void RunSampleAndCompareResults(string programResourceName) + { + RunSampleAndCompareResults(programResourceName, programResourceName); + } + + /// + /// Load sample Refal program or output from assembly resources + /// + string LoadResourceText(string resourceName) + { + var asm = this.GetType().Assembly; + + using (var stream = asm.GetManifestResourceStream(BaseResourceName + resourceName)) + { + Assert.IsNotNull(stream); + + using (var sr = new StreamReader(stream)) + { + var s = sr.ReadToEnd(); + Assert.IsFalse(string.IsNullOrEmpty(s)); + + s = Regex.Replace(s, @"\r\n?", Environment.NewLine); + return s; + } + } + } + } +} diff --git a/Languages/Refal/UnitTests/Sources/99-bottles-v1.ref b/Languages/Refal/UnitTests/Sources/99-bottles-v1.ref new file mode 100644 index 0000000..45c304c --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/99-bottles-v1.ref @@ -0,0 +1,29 @@ +* http://99-bottles-of-beer.net/language-refal-1725.html +* Replaced "\n" with to improve portability +* ----------------------------------------------------- + +* Refal is "Recursive functions algorithmic language" +* and is a pattern matching programming language. +* This entry uses both recursion and pattern matching +* capabilities of refal. + +$ENTRY Go { = ;}; + +Sing { + s.N = ; +}; + +Beer { + 0 = "No more bottles of beer"; + 1 = "One bottle of beer"; + s.N = s.N "bottles of beer"; +}; + +Verse { + 0 = ""; + s.N = "on the wall."> + + > "on the wall"> + + >; +}; diff --git a/Languages/Refal/UnitTests/Sources/99-bottles-v1.txt b/Languages/Refal/UnitTests/Sources/99-bottles-v1.txt new file mode 100644 index 0000000..d12ba49 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/99-bottles-v1.txt @@ -0,0 +1,396 @@ +99 bottles of beer on the wall. +Take one down, pass it around, +98 bottles of beer on the wall + +98 bottles of beer on the wall. +Take one down, pass it around, +97 bottles of beer on the wall + +97 bottles of beer on the wall. +Take one down, pass it around, +96 bottles of beer on the wall + +96 bottles of beer on the wall. +Take one down, pass it around, +95 bottles of beer on the wall + +95 bottles of beer on the wall. +Take one down, pass it around, +94 bottles of beer on the wall + +94 bottles of beer on the wall. +Take one down, pass it around, +93 bottles of beer on the wall + +93 bottles of beer on the wall. +Take one down, pass it around, +92 bottles of beer on the wall + +92 bottles of beer on the wall. +Take one down, pass it around, +91 bottles of beer on the wall + +91 bottles of beer on the wall. +Take one down, pass it around, +90 bottles of beer on the wall + +90 bottles of beer on the wall. +Take one down, pass it around, +89 bottles of beer on the wall + +89 bottles of beer on the wall. +Take one down, pass it around, +88 bottles of beer on the wall + +88 bottles of beer on the wall. +Take one down, pass it around, +87 bottles of beer on the wall + +87 bottles of beer on the wall. +Take one down, pass it around, +86 bottles of beer on the wall + +86 bottles of beer on the wall. +Take one down, pass it around, +85 bottles of beer on the wall + +85 bottles of beer on the wall. +Take one down, pass it around, +84 bottles of beer on the wall + +84 bottles of beer on the wall. +Take one down, pass it around, +83 bottles of beer on the wall + +83 bottles of beer on the wall. +Take one down, pass it around, +82 bottles of beer on the wall + +82 bottles of beer on the wall. +Take one down, pass it around, +81 bottles of beer on the wall + +81 bottles of beer on the wall. +Take one down, pass it around, +80 bottles of beer on the wall + +80 bottles of beer on the wall. +Take one down, pass it around, +79 bottles of beer on the wall + +79 bottles of beer on the wall. +Take one down, pass it around, +78 bottles of beer on the wall + +78 bottles of beer on the wall. +Take one down, pass it around, +77 bottles of beer on the wall + +77 bottles of beer on the wall. +Take one down, pass it around, +76 bottles of beer on the wall + +76 bottles of beer on the wall. +Take one down, pass it around, +75 bottles of beer on the wall + +75 bottles of beer on the wall. +Take one down, pass it around, +74 bottles of beer on the wall + +74 bottles of beer on the wall. +Take one down, pass it around, +73 bottles of beer on the wall + +73 bottles of beer on the wall. +Take one down, pass it around, +72 bottles of beer on the wall + +72 bottles of beer on the wall. +Take one down, pass it around, +71 bottles of beer on the wall + +71 bottles of beer on the wall. +Take one down, pass it around, +70 bottles of beer on the wall + +70 bottles of beer on the wall. +Take one down, pass it around, +69 bottles of beer on the wall + +69 bottles of beer on the wall. +Take one down, pass it around, +68 bottles of beer on the wall + +68 bottles of beer on the wall. +Take one down, pass it around, +67 bottles of beer on the wall + +67 bottles of beer on the wall. +Take one down, pass it around, +66 bottles of beer on the wall + +66 bottles of beer on the wall. +Take one down, pass it around, +65 bottles of beer on the wall + +65 bottles of beer on the wall. +Take one down, pass it around, +64 bottles of beer on the wall + +64 bottles of beer on the wall. +Take one down, pass it around, +63 bottles of beer on the wall + +63 bottles of beer on the wall. +Take one down, pass it around, +62 bottles of beer on the wall + +62 bottles of beer on the wall. +Take one down, pass it around, +61 bottles of beer on the wall + +61 bottles of beer on the wall. +Take one down, pass it around, +60 bottles of beer on the wall + +60 bottles of beer on the wall. +Take one down, pass it around, +59 bottles of beer on the wall + +59 bottles of beer on the wall. +Take one down, pass it around, +58 bottles of beer on the wall + +58 bottles of beer on the wall. +Take one down, pass it around, +57 bottles of beer on the wall + +57 bottles of beer on the wall. +Take one down, pass it around, +56 bottles of beer on the wall + +56 bottles of beer on the wall. +Take one down, pass it around, +55 bottles of beer on the wall + +55 bottles of beer on the wall. +Take one down, pass it around, +54 bottles of beer on the wall + +54 bottles of beer on the wall. +Take one down, pass it around, +53 bottles of beer on the wall + +53 bottles of beer on the wall. +Take one down, pass it around, +52 bottles of beer on the wall + +52 bottles of beer on the wall. +Take one down, pass it around, +51 bottles of beer on the wall + +51 bottles of beer on the wall. +Take one down, pass it around, +50 bottles of beer on the wall + +50 bottles of beer on the wall. +Take one down, pass it around, +49 bottles of beer on the wall + +49 bottles of beer on the wall. +Take one down, pass it around, +48 bottles of beer on the wall + +48 bottles of beer on the wall. +Take one down, pass it around, +47 bottles of beer on the wall + +47 bottles of beer on the wall. +Take one down, pass it around, +46 bottles of beer on the wall + +46 bottles of beer on the wall. +Take one down, pass it around, +45 bottles of beer on the wall + +45 bottles of beer on the wall. +Take one down, pass it around, +44 bottles of beer on the wall + +44 bottles of beer on the wall. +Take one down, pass it around, +43 bottles of beer on the wall + +43 bottles of beer on the wall. +Take one down, pass it around, +42 bottles of beer on the wall + +42 bottles of beer on the wall. +Take one down, pass it around, +41 bottles of beer on the wall + +41 bottles of beer on the wall. +Take one down, pass it around, +40 bottles of beer on the wall + +40 bottles of beer on the wall. +Take one down, pass it around, +39 bottles of beer on the wall + +39 bottles of beer on the wall. +Take one down, pass it around, +38 bottles of beer on the wall + +38 bottles of beer on the wall. +Take one down, pass it around, +37 bottles of beer on the wall + +37 bottles of beer on the wall. +Take one down, pass it around, +36 bottles of beer on the wall + +36 bottles of beer on the wall. +Take one down, pass it around, +35 bottles of beer on the wall + +35 bottles of beer on the wall. +Take one down, pass it around, +34 bottles of beer on the wall + +34 bottles of beer on the wall. +Take one down, pass it around, +33 bottles of beer on the wall + +33 bottles of beer on the wall. +Take one down, pass it around, +32 bottles of beer on the wall + +32 bottles of beer on the wall. +Take one down, pass it around, +31 bottles of beer on the wall + +31 bottles of beer on the wall. +Take one down, pass it around, +30 bottles of beer on the wall + +30 bottles of beer on the wall. +Take one down, pass it around, +29 bottles of beer on the wall + +29 bottles of beer on the wall. +Take one down, pass it around, +28 bottles of beer on the wall + +28 bottles of beer on the wall. +Take one down, pass it around, +27 bottles of beer on the wall + +27 bottles of beer on the wall. +Take one down, pass it around, +26 bottles of beer on the wall + +26 bottles of beer on the wall. +Take one down, pass it around, +25 bottles of beer on the wall + +25 bottles of beer on the wall. +Take one down, pass it around, +24 bottles of beer on the wall + +24 bottles of beer on the wall. +Take one down, pass it around, +23 bottles of beer on the wall + +23 bottles of beer on the wall. +Take one down, pass it around, +22 bottles of beer on the wall + +22 bottles of beer on the wall. +Take one down, pass it around, +21 bottles of beer on the wall + +21 bottles of beer on the wall. +Take one down, pass it around, +20 bottles of beer on the wall + +20 bottles of beer on the wall. +Take one down, pass it around, +19 bottles of beer on the wall + +19 bottles of beer on the wall. +Take one down, pass it around, +18 bottles of beer on the wall + +18 bottles of beer on the wall. +Take one down, pass it around, +17 bottles of beer on the wall + +17 bottles of beer on the wall. +Take one down, pass it around, +16 bottles of beer on the wall + +16 bottles of beer on the wall. +Take one down, pass it around, +15 bottles of beer on the wall + +15 bottles of beer on the wall. +Take one down, pass it around, +14 bottles of beer on the wall + +14 bottles of beer on the wall. +Take one down, pass it around, +13 bottles of beer on the wall + +13 bottles of beer on the wall. +Take one down, pass it around, +12 bottles of beer on the wall + +12 bottles of beer on the wall. +Take one down, pass it around, +11 bottles of beer on the wall + +11 bottles of beer on the wall. +Take one down, pass it around, +10 bottles of beer on the wall + +10 bottles of beer on the wall. +Take one down, pass it around, +9 bottles of beer on the wall + +9 bottles of beer on the wall. +Take one down, pass it around, +8 bottles of beer on the wall + +8 bottles of beer on the wall. +Take one down, pass it around, +7 bottles of beer on the wall + +7 bottles of beer on the wall. +Take one down, pass it around, +6 bottles of beer on the wall + +6 bottles of beer on the wall. +Take one down, pass it around, +5 bottles of beer on the wall + +5 bottles of beer on the wall. +Take one down, pass it around, +4 bottles of beer on the wall + +4 bottles of beer on the wall. +Take one down, pass it around, +3 bottles of beer on the wall + +3 bottles of beer on the wall. +Take one down, pass it around, +2 bottles of beer on the wall + +2 bottles of beer on the wall. +Take one down, pass it around, +One bottle of beer on the wall + +One bottle of beer on the wall. +Take one down, pass it around, +No more bottles of beer on the wall + diff --git a/Languages/Refal/UnitTests/Sources/99-bottles-v2.ref b/Languages/Refal/UnitTests/Sources/99-bottles-v2.ref new file mode 100644 index 0000000..dbd74a0 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/99-bottles-v2.ref @@ -0,0 +1,31 @@ +* http://99-bottles-of-beer.net/language-refal5-491.html +* Removed infinite loop, replaced '\n' with +* ----------------------------------------------------- + +* Refal5 version of famous lyrics +* Created and debugged carefully by Dendik (ru.pochtamt[at]dendik) +* You can get some info on Refal at http://refal.org/index_e.htm + +* Bronikkk: Something like that, maybe? + +$ENTRY Go { = ; 100=; t.n = >; }; + +Do +{ + "next" t.n, <- (t.n) 1> : { t.m = t.m; '-' 1 = 100; }; + "look" t.n = >; + "drink" t.n = > t.n; + "Look" t.n = ', ' ; + "Drink" 100 = 'Go to the store, buy some more, ' ; + "Drink" t.n = 'Take one down, pass it around, ' ; + "wall" = ' on the wall'; + "Beer" 0 = 'No more ' ; + "Beer" 1 = 1 ; + "Beer" t.n = t.n ; + "beer" 0 = 'no more ' ; + "beer" 1 = 1 ; + "beer" t.n = t.n ; + "beer" = 'bottles of beer'; + "beer1" = 'bottle of beer'; + t.n = >; +}; diff --git a/Languages/Refal/UnitTests/Sources/99-bottles-v2.txt b/Languages/Refal/UnitTests/Sources/99-bottles-v2.txt new file mode 100644 index 0000000..20cc8a7 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/99-bottles-v2.txt @@ -0,0 +1,300 @@ +99 bottles of beer on the wall, 99 bottles of beer +Take one down, pass it around, 98 bottles of beer + +98 bottles of beer on the wall, 98 bottles of beer +Take one down, pass it around, 97 bottles of beer + +97 bottles of beer on the wall, 97 bottles of beer +Take one down, pass it around, 96 bottles of beer + +96 bottles of beer on the wall, 96 bottles of beer +Take one down, pass it around, 95 bottles of beer + +95 bottles of beer on the wall, 95 bottles of beer +Take one down, pass it around, 94 bottles of beer + +94 bottles of beer on the wall, 94 bottles of beer +Take one down, pass it around, 93 bottles of beer + +93 bottles of beer on the wall, 93 bottles of beer +Take one down, pass it around, 92 bottles of beer + +92 bottles of beer on the wall, 92 bottles of beer +Take one down, pass it around, 91 bottles of beer + +91 bottles of beer on the wall, 91 bottles of beer +Take one down, pass it around, 90 bottles of beer + +90 bottles of beer on the wall, 90 bottles of beer +Take one down, pass it around, 89 bottles of beer + +89 bottles of beer on the wall, 89 bottles of beer +Take one down, pass it around, 88 bottles of beer + +88 bottles of beer on the wall, 88 bottles of beer +Take one down, pass it around, 87 bottles of beer + +87 bottles of beer on the wall, 87 bottles of beer +Take one down, pass it around, 86 bottles of beer + +86 bottles of beer on the wall, 86 bottles of beer +Take one down, pass it around, 85 bottles of beer + +85 bottles of beer on the wall, 85 bottles of beer +Take one down, pass it around, 84 bottles of beer + +84 bottles of beer on the wall, 84 bottles of beer +Take one down, pass it around, 83 bottles of beer + +83 bottles of beer on the wall, 83 bottles of beer +Take one down, pass it around, 82 bottles of beer + +82 bottles of beer on the wall, 82 bottles of beer +Take one down, pass it around, 81 bottles of beer + +81 bottles of beer on the wall, 81 bottles of beer +Take one down, pass it around, 80 bottles of beer + +80 bottles of beer on the wall, 80 bottles of beer +Take one down, pass it around, 79 bottles of beer + +79 bottles of beer on the wall, 79 bottles of beer +Take one down, pass it around, 78 bottles of beer + +78 bottles of beer on the wall, 78 bottles of beer +Take one down, pass it around, 77 bottles of beer + +77 bottles of beer on the wall, 77 bottles of beer +Take one down, pass it around, 76 bottles of beer + +76 bottles of beer on the wall, 76 bottles of beer +Take one down, pass it around, 75 bottles of beer + +75 bottles of beer on the wall, 75 bottles of beer +Take one down, pass it around, 74 bottles of beer + +74 bottles of beer on the wall, 74 bottles of beer +Take one down, pass it around, 73 bottles of beer + +73 bottles of beer on the wall, 73 bottles of beer +Take one down, pass it around, 72 bottles of beer + +72 bottles of beer on the wall, 72 bottles of beer +Take one down, pass it around, 71 bottles of beer + +71 bottles of beer on the wall, 71 bottles of beer +Take one down, pass it around, 70 bottles of beer + +70 bottles of beer on the wall, 70 bottles of beer +Take one down, pass it around, 69 bottles of beer + +69 bottles of beer on the wall, 69 bottles of beer +Take one down, pass it around, 68 bottles of beer + +68 bottles of beer on the wall, 68 bottles of beer +Take one down, pass it around, 67 bottles of beer + +67 bottles of beer on the wall, 67 bottles of beer +Take one down, pass it around, 66 bottles of beer + +66 bottles of beer on the wall, 66 bottles of beer +Take one down, pass it around, 65 bottles of beer + +65 bottles of beer on the wall, 65 bottles of beer +Take one down, pass it around, 64 bottles of beer + +64 bottles of beer on the wall, 64 bottles of beer +Take one down, pass it around, 63 bottles of beer + +63 bottles of beer on the wall, 63 bottles of beer +Take one down, pass it around, 62 bottles of beer + +62 bottles of beer on the wall, 62 bottles of beer +Take one down, pass it around, 61 bottles of beer + +61 bottles of beer on the wall, 61 bottles of beer +Take one down, pass it around, 60 bottles of beer + +60 bottles of beer on the wall, 60 bottles of beer +Take one down, pass it around, 59 bottles of beer + +59 bottles of beer on the wall, 59 bottles of beer +Take one down, pass it around, 58 bottles of beer + +58 bottles of beer on the wall, 58 bottles of beer +Take one down, pass it around, 57 bottles of beer + +57 bottles of beer on the wall, 57 bottles of beer +Take one down, pass it around, 56 bottles of beer + +56 bottles of beer on the wall, 56 bottles of beer +Take one down, pass it around, 55 bottles of beer + +55 bottles of beer on the wall, 55 bottles of beer +Take one down, pass it around, 54 bottles of beer + +54 bottles of beer on the wall, 54 bottles of beer +Take one down, pass it around, 53 bottles of beer + +53 bottles of beer on the wall, 53 bottles of beer +Take one down, pass it around, 52 bottles of beer + +52 bottles of beer on the wall, 52 bottles of beer +Take one down, pass it around, 51 bottles of beer + +51 bottles of beer on the wall, 51 bottles of beer +Take one down, pass it around, 50 bottles of beer + +50 bottles of beer on the wall, 50 bottles of beer +Take one down, pass it around, 49 bottles of beer + +49 bottles of beer on the wall, 49 bottles of beer +Take one down, pass it around, 48 bottles of beer + +48 bottles of beer on the wall, 48 bottles of beer +Take one down, pass it around, 47 bottles of beer + +47 bottles of beer on the wall, 47 bottles of beer +Take one down, pass it around, 46 bottles of beer + +46 bottles of beer on the wall, 46 bottles of beer +Take one down, pass it around, 45 bottles of beer + +45 bottles of beer on the wall, 45 bottles of beer +Take one down, pass it around, 44 bottles of beer + +44 bottles of beer on the wall, 44 bottles of beer +Take one down, pass it around, 43 bottles of beer + +43 bottles of beer on the wall, 43 bottles of beer +Take one down, pass it around, 42 bottles of beer + +42 bottles of beer on the wall, 42 bottles of beer +Take one down, pass it around, 41 bottles of beer + +41 bottles of beer on the wall, 41 bottles of beer +Take one down, pass it around, 40 bottles of beer + +40 bottles of beer on the wall, 40 bottles of beer +Take one down, pass it around, 39 bottles of beer + +39 bottles of beer on the wall, 39 bottles of beer +Take one down, pass it around, 38 bottles of beer + +38 bottles of beer on the wall, 38 bottles of beer +Take one down, pass it around, 37 bottles of beer + +37 bottles of beer on the wall, 37 bottles of beer +Take one down, pass it around, 36 bottles of beer + +36 bottles of beer on the wall, 36 bottles of beer +Take one down, pass it around, 35 bottles of beer + +35 bottles of beer on the wall, 35 bottles of beer +Take one down, pass it around, 34 bottles of beer + +34 bottles of beer on the wall, 34 bottles of beer +Take one down, pass it around, 33 bottles of beer + +33 bottles of beer on the wall, 33 bottles of beer +Take one down, pass it around, 32 bottles of beer + +32 bottles of beer on the wall, 32 bottles of beer +Take one down, pass it around, 31 bottles of beer + +31 bottles of beer on the wall, 31 bottles of beer +Take one down, pass it around, 30 bottles of beer + +30 bottles of beer on the wall, 30 bottles of beer +Take one down, pass it around, 29 bottles of beer + +29 bottles of beer on the wall, 29 bottles of beer +Take one down, pass it around, 28 bottles of beer + +28 bottles of beer on the wall, 28 bottles of beer +Take one down, pass it around, 27 bottles of beer + +27 bottles of beer on the wall, 27 bottles of beer +Take one down, pass it around, 26 bottles of beer + +26 bottles of beer on the wall, 26 bottles of beer +Take one down, pass it around, 25 bottles of beer + +25 bottles of beer on the wall, 25 bottles of beer +Take one down, pass it around, 24 bottles of beer + +24 bottles of beer on the wall, 24 bottles of beer +Take one down, pass it around, 23 bottles of beer + +23 bottles of beer on the wall, 23 bottles of beer +Take one down, pass it around, 22 bottles of beer + +22 bottles of beer on the wall, 22 bottles of beer +Take one down, pass it around, 21 bottles of beer + +21 bottles of beer on the wall, 21 bottles of beer +Take one down, pass it around, 20 bottles of beer + +20 bottles of beer on the wall, 20 bottles of beer +Take one down, pass it around, 19 bottles of beer + +19 bottles of beer on the wall, 19 bottles of beer +Take one down, pass it around, 18 bottles of beer + +18 bottles of beer on the wall, 18 bottles of beer +Take one down, pass it around, 17 bottles of beer + +17 bottles of beer on the wall, 17 bottles of beer +Take one down, pass it around, 16 bottles of beer + +16 bottles of beer on the wall, 16 bottles of beer +Take one down, pass it around, 15 bottles of beer + +15 bottles of beer on the wall, 15 bottles of beer +Take one down, pass it around, 14 bottles of beer + +14 bottles of beer on the wall, 14 bottles of beer +Take one down, pass it around, 13 bottles of beer + +13 bottles of beer on the wall, 13 bottles of beer +Take one down, pass it around, 12 bottles of beer + +12 bottles of beer on the wall, 12 bottles of beer +Take one down, pass it around, 11 bottles of beer + +11 bottles of beer on the wall, 11 bottles of beer +Take one down, pass it around, 10 bottles of beer + +10 bottles of beer on the wall, 10 bottles of beer +Take one down, pass it around, 9 bottles of beer + +9 bottles of beer on the wall, 9 bottles of beer +Take one down, pass it around, 8 bottles of beer + +8 bottles of beer on the wall, 8 bottles of beer +Take one down, pass it around, 7 bottles of beer + +7 bottles of beer on the wall, 7 bottles of beer +Take one down, pass it around, 6 bottles of beer + +6 bottles of beer on the wall, 6 bottles of beer +Take one down, pass it around, 5 bottles of beer + +5 bottles of beer on the wall, 5 bottles of beer +Take one down, pass it around, 4 bottles of beer + +4 bottles of beer on the wall, 4 bottles of beer +Take one down, pass it around, 3 bottles of beer + +3 bottles of beer on the wall, 3 bottles of beer +Take one down, pass it around, 2 bottles of beer + +2 bottles of beer on the wall, 2 bottles of beer +Take one down, pass it around, 1 bottle of beer + +1 bottle of beer on the wall, 1 bottle of beer +Take one down, pass it around, no more bottles of beer + +No more bottles of beer on the wall, no more bottles of beer +Go to the store, buy some more, 99 bottles of beer + diff --git a/Languages/Refal/UnitTests/Sources/arith.ref b/Languages/Refal/UnitTests/Sources/arith.ref new file mode 100644 index 0000000..c9fd970 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/arith.ref @@ -0,0 +1,154 @@ + +* This is a test of the Refal-5 system. +* The program asks for an arithmetic expression +* and translates in into a code for a one-address computer. + +* Example taken from http://botik.ru/pub/local/scp/refal5/refal5.html +* Removed keyboard input to make it run from Irony Grammar Explorer + +$ENTRY Go { = ; }; + +Expression { = 'Joe^2 * 5 / Markus^(Carol + 318)'; } + +Introduction { = + + + + + + >; + }; + +* Loop: in and out +Loop { = > >; }; + +* anNIhiLate +Nil {e.X = ;}; + +Inout { + 0 = ; + e.X = >>; + }; + +Out { + = /**/; +e.1 = + /**/; }; + +* Lexical analysis of input string. Nested multibracket format. +Lex { + (e.1) '('e.2 = ; + ((e.1)e.2) ')'e.3 = ; + ('$'e.1) ')'e.3 = + ; + (e.1) ' ' e.2 = ; + (e.1) '\t' e.2 = ; + (e.1) '\'' e.2, : + { () e.3 = ; + (e.0) e.3 = ; + }; + + (e.1) s.A e.2, : + {'L' e.A1, : (e.Id) e.3 = + ) e.3>; + 'D' e.A1, : (e.D-Str) e.3 = + ) e.3>; + s.T e.A1 = ; + }; + + ('$'e.1) = e.1; + (e.M) = + ; + }; + +* Inside quotes +Quotes { + s.Q (e.1) e.0 '\\' '\\' e.2 = ; + s.Q (e.1) e.0 '\\' s.Q e.2 = ; + s.Q (e.1) e.0 s.Q e.2 = (e.1 e.0) e.2; + s.Q (e.1) e.2 = () e.1 e.2; + }; + +* chop off IDentifier TAIL +Id-tail { + (e.1) s.A e.2, : + {'L' e.3 = ; + 'D' e.3 = ; + s.T e.3 = (e.1) s.A e.2; + }; + (e.1) = (e.1); + }; + +* chop off Digit-STRING +D-string { + (e.1) s.A e.2, : 'D' e.A1 = ; + (e.1) e.2 = (e.1) e.2; + }; + +* PRint Left MultiBracket (nested format) +Pr-lmb { + (e.1) e.2 = ; + '$'e.1 = ; + }; + + +Translate + { = ; + e.1 = >; }; + +Last1 { + (e.A s.X e.B) e.1 s.X(e.2) = e.1 s.X(e.2); + (e.A) e.1 t.X(e.2) = ; + (e.A) (e.2) = (e.2); + }; + +Parse { + e.Exp, : e.1 s.Op(e.2) = + s.Op() ; + e.Exp, : e.1 s.Op(e.2) = + s.Op() ; + e.1 '^' e.2 = '^'() ; + s.Symb, : 'Wi' e.S = s.Symb; + s.Symb, : 'N' e.S = s.Symb; + (e.Exp) = ; + = ; + e.Exp = +
; + }; + +Code-gen { + e.1 'fail' = ; + (s.N) '-'()e.2 = + 'Minus ;' ; + (s.N) s.Op(e.1)s.2 = + ';' ; + (s.N) '+'(s.1)e.2 = + ';' ; + (s.N) '*'(s.1)e.2 = + ';' ; + (s.N) s.Op(e.1)e.2 = + 'STORE\tR+'';' + ) e.1> + 'R+'';' ; + (s.N) s.Symb = 'LOAD\t' ';' ; + (s.N) e.X = ('Syntax error')';' ; + }; + +Outform { s.S, : + {'Wi' e.S1 = ; + 'N' e.S1 = ; }; + }; + +Code-op { + '+' = 'ADD\t'; + '-' = 'SUB\t'; + '*' = 'MUL\t'; + '/' = 'DIV\t'; + '^' = 'POW\t'; + }; + +Write { + (e.1)';' e.2 = ; /* Syntax error */ + e.1';' e.2 = ; + = ; + }; \ No newline at end of file diff --git a/Languages/Refal/UnitTests/Sources/arith.txt b/Languages/Refal/UnitTests/Sources/arith.txt new file mode 100644 index 0000000..68f3801 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/arith.txt @@ -0,0 +1,18 @@ +This is a program to translate an arithmetic expression +into a code for a one-address computer. Primary operands +are identifiers and whole numbers. Operations are: ++, -, *, /, ^ with usual priorities. Parentheses as usual. + +Source expression: Joe^2 * 5 / Markus^(Carol + 318) +The translation is: + + LOAD Carol + ADD 318 + STORE R+1 + LOAD Markus + POW R+1 + STORE R+1 + LOAD Joe + POW 2 + MUL 5 + DIV R+1 diff --git a/Languages/Refal/UnitTests/Sources/binary.ref b/Languages/Refal/UnitTests/Sources/binary.ref new file mode 100644 index 0000000..d74454f --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/binary.ref @@ -0,0 +1,31 @@ + +* Adds two binary numbers +* Written by Y [14-06-03] + +$ENTRY Go { = + ; + 24 = ; + s.1 = >>; +} + +WrapData { + s.1 = s.1 "=" ";"; +} + +Data { +1 = '* Quine (simple version)'; +2 = '* Written by Alexey Yakovlev '; +3 = ''; +4 = '$ENTRY Go { ='; +5 = ' '; +6 = ' ;'; +7 = '}'; +8 = ''; +9 = 'PrintProgram {'; +10 = ' 24 =;'; +11 = ' s.1 = >>;'; +12 = '}'; +13 = ''; +14 = 'PrintData { ='; +15 = ' ;'; +16 = ' 24 = ;'; +17 = ' s.1 = >>;'; +18 = '}'; +19 = ''; +20 = 'WrapData {'; +21 = ' s.1 = s.1 "=" ";";'; +22 = '}'; +23 = ''; +} diff --git a/Languages/Refal/UnitTests/Sources/quine-xplained.ref b/Languages/Refal/UnitTests/Sources/quine-xplained.ref new file mode 100644 index 0000000..a6f94c3 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/quine-xplained.ref @@ -0,0 +1,28 @@ +* Quine explained +* Written by Alexey Yakovlev + +* InsideOut expands (e.1)(e.2) into e.1(e.1)(e.2)e.2 +* Quote adds quotes and braces around an expression +* NewLine converts percents into new lines char by char + +$ENTRY Go { = + %%* InsideOut expands (e.1)(e.2) into e.1(e.1)(e.2)e.2%* Quote adds quotes and braces around an expression%* NewLine converts percents into new lines char by char%%$ENTRY Go { =% % >%}%%InsideOut {% (e.1)(e.2) = ;%}%%Quote {% e.1 = (e.1)%}%%NewLine {% s.1 e.2, : s.1 = ;% s.1 e.2 = s.1 ;% =;%}') + > + > +} + +InsideOut { + (e.1)(e.2) = ; +} + +Quote { + e.1 = (e.1) +} + +NewLine { + s.1 e.2, : s.1 = ; + s.1 e.2 = s.1 ; + =; +} diff --git a/Languages/Refal/UnitTests/Sources/xtras-bigint.ref b/Languages/Refal/UnitTests/Sources/xtras-bigint.ref new file mode 100644 index 0000000..b133f86 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/xtras-bigint.ref @@ -0,0 +1,9 @@ +* BigInteger arithmetics demonstration +* Written by Y [07-02-10] + +$ENTRY Go { = + > +} diff --git a/Languages/Refal/UnitTests/Sources/xtras-bigint.txt b/Languages/Refal/UnitTests/Sources/xtras-bigint.txt new file mode 100644 index 0000000..e143451 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/xtras-bigint.txt @@ -0,0 +1 @@ +50 diff --git a/Languages/Refal/UnitTests/Sources/xtras-factorial.ref b/Languages/Refal/UnitTests/Sources/xtras-factorial.ref new file mode 100644 index 0000000..4c00aae --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/xtras-factorial.ref @@ -0,0 +1,11 @@ +* Factorial using BigInteger arithmetics +* Written by Y [07-02-10] + +$ENTRY Go { = + >; +} + +Fact { + 0 = 1; + s.N = <* s.N >>; +} diff --git a/Languages/Refal/UnitTests/Sources/xtras-factorial.txt b/Languages/Refal/UnitTests/Sources/xtras-factorial.txt new file mode 100644 index 0000000..3784890 --- /dev/null +++ b/Languages/Refal/UnitTests/Sources/xtras-factorial.txt @@ -0,0 +1 @@ +1220136825991110068701238785423046926253574342803192842192413588385845373153881997605496447502203281863013616477148203584163378722078177200480785205159329285477907571939330603772960859086270429174547882424912726344305670173270769461062802310452644218878789465754777149863494367781037644274033827365397471386477878495438489595537537990423241061271326984327745715546309977202781014561081188373709531016356324432987029563896628911658974769572087926928871281780070265174507768410719624390394322536422605234945850129918571501248706961568141625359056693423813008856249246891564126775654481886506593847951775360894005745238940335798476363944905313062323749066445048824665075946735862074637925184200459369692981022263971952597190945217823331756934581508552332820762820023402626907898342451712006207714640979456116127629145951237229913340169552363850942885592018727433795173014586357570828355780158735432768888680120399882384702151467605445407663535984174430480128938313896881639487469658817504506926365338175055478128640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..d162643 --- /dev/null +++ b/License.txt @@ -0,0 +1,16 @@ +Copyright (c) 20012 Roman Ivantsov + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software +and associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..e9f6d25 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,43 @@ +ReadMe file for Irony project +http://www.codeplex.com/irony + +Prerequisites + OS: Windows 7; + Visual Studio 2010, .NET Framework 4.0 + +Demo instructions +To run Grammar Explorer +* Open Irony_All.sln solution file in Visual Studio. +* Right-click on the project "030.Irony.GrammarExplorer" and select "Set as StartUp project" from context menu. +* Click Run button on toolbar (F5). Grammar Explorer Window opens. +* !!!Important: if you are launching the Grammar Explorer for the first time after downloading Irony and + if you see that Grammars combobox is not empty, then make sure you clear it: click the button + next to the combobox and select "Remove all" +* If Grammars combobox on top is empty, click on the button next to it (or right-click the combobox) and select + "Add grammar" command. In the file-open window that appears, navigate to (root)\Irony.Samples\bin\debug folder and + select Irony.Samples.dll. Application will popup a small window with a list of grammars in selected assembly. + Leave all lines checked and click "OK". The newly added grammars will appear in the grammar combobox. +* Select grammar/language in top combo-box. +* Browse form tabs to see grammar data. +* To parse source code sample, switch to "Test" tab. Click "Load..." button on top of the form. Open file dialog opens. +* Navigate to \Irony.Samples\SourceSamples folder. Select source file appropriate for the selected grammar. +* Source file contents are loaded in the text area in the form. Click Parse button to parse the sample. +* The Parse Tree control on the right will show the parse tree for the sample. +* If the button "Run" enabled, click it to execute/evaluate the code. The results are shown in the Output window at the bottom of the form. + For Expression Evaluator grammar, the output is the result of the last expression or assignment. Interpreter for Scheme + can execute more complex programs found in SourceSamples\Scheme subfolder. +* Alternatively you can paste or type your own sample program into source text area. +* Repeat for other selections in the Grammar combobox. +* Grammar Explorer restores your last language selection and source sample after you close/restart the form. + +Note about Silverlight: +IronySilverlight project in the solution is a version of Irony for use in Silverlight environment. +This project shares almost all sources files with core Irony project. It has a conditonal symbol SILVERLIGHT defined +and uses it in a few places to select different code pieces for different environments. +The output assembly name is IronySL.dll. By default this project is not compiled when you build the solution - +so it does not break your build if you don't have SL files on your computer. +To compile this project you must have Silverlight 4 Tools installed: +(http://www.microsoft.com/downloads/details.aspx?familyid=9442b0f2-7465-417a-88f3-5e7b5409e9dd&displaylang=en) +Credit for Irony-SL adaptation goes to Kirill Osenkov + + \ No newline at end of file From 1411cbde98b210ad25366d7a94bed301f88e0f35 Mon Sep 17 00:00:00 2001 From: Alexey Yakovlev Date: Fri, 9 Mar 2012 04:14:18 +0400 Subject: [PATCH 2/5] Improved dependency resolution for grammar assemblies. For the first time the assembly is always loaded using LoadFrom(fileName) to enable standard .NET dependency resolution policy. If dependent assembly couldn't be located, GrammarExplorer asks the user to browse for a file, like .NET Reflector does. --- Irony.GrammarExplorer/GrammarLoader.cs | 102 +++++++++++++++++-------- 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/Irony.GrammarExplorer/GrammarLoader.cs b/Irony.GrammarExplorer/GrammarLoader.cs index 0b1b079..e7dceb2 100644 --- a/Irony.GrammarExplorer/GrammarLoader.cs +++ b/Irony.GrammarExplorer/GrammarLoader.cs @@ -19,42 +19,64 @@ using Irony.Parsing; using System.IO; using System.Threading; +using System.Windows.Forms; namespace Irony.GrammarExplorer { /// /// Maintains grammar assemblies, reloads updated files automatically. /// class GrammarLoader { - private TimeSpan _autoRefreshDelay = TimeSpan.FromMilliseconds(500); + private TimeSpan _autoRefreshDelay = TimeSpan.FromMilliseconds(1000); private Dictionary _cachedGrammarAssemblies = new Dictionary(); private static HashSet _probingPaths = new HashSet(); - private static Dictionary _loadedAssemblies = new Dictionary(); - private static bool _enableAssemblyResolution = false; + private static Dictionary _loadedAssembliesByLocation = new Dictionary(); + private static Dictionary _loadedAssembliesByNames = new Dictionary(); + private static bool _enableBrowsingForAssemblyResolution = false; static GrammarLoader() { - AppDomain.CurrentDomain.AssemblyLoad += (s, args) => _loadedAssemblies[args.LoadedAssembly.FullName] = args.LoadedAssembly; - AppDomain.CurrentDomain.AssemblyResolve += (s, args) => _enableAssemblyResolution ? FindAssembly(args.Name) : null; + AppDomain.CurrentDomain.AssemblyLoad += (sender, args) => _loadedAssembliesByNames[args.LoadedAssembly.FullName] = args.LoadedAssembly; + AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => FindAssembly(args.Name); } static Assembly FindAssembly(string assemblyName) { - if (_loadedAssemblies.ContainsKey(assemblyName)) { - return _loadedAssemblies[assemblyName]; - } - // use probing paths to look for assemblies + if (_loadedAssembliesByNames.ContainsKey(assemblyName)) + return _loadedAssembliesByNames[assemblyName]; + // ignore resource assemblies + if (assemblyName.ToLower().Contains(".resources, version=")) + return _loadedAssembliesByNames[assemblyName] = null; + // use probing paths to look for dependency assemblies var fileName = assemblyName.Split(',').First() + ".dll"; foreach (var path in _probingPaths) { var fullName = Path.Combine(path, fileName); if (File.Exists(fullName)) { try { - return _loadedAssemblies[assemblyName] = Assembly.LoadFrom(fullName); + return LoadAssembly(fullName); } catch { // the file seems to be bad, let's try to find another one } } } + // the last chance: try asking user to locate the assembly + if (_enableBrowsingForAssemblyResolution) { + fileName = BrowseFor(assemblyName); + if (!string.IsNullOrWhiteSpace(fileName)) + return LoadAssembly(fileName); + } // assembly not found, don't search for it again - return _loadedAssemblies[assemblyName] = null; + return _loadedAssembliesByNames[assemblyName] = null; + } + + static string BrowseFor(string assemblyName) { + var fileDialog = new OpenFileDialog { + Title = "Please locate assembly: " + assemblyName, + Filter = "Assemblies (*.dll)|*.dll|All files (*.*)|*.*" + }; + using (fileDialog) { + if (fileDialog.ShowDialog() == DialogResult.OK) + return fileDialog.FileName; + } + return null; } class CachedAssembly { @@ -62,6 +84,7 @@ class CachedAssembly { public DateTime LastWriteTime; public FileSystemWatcher Watcher; public Assembly Assembly; + public bool UpdateScheduled; } public event EventHandler AssemblyUpdated; @@ -73,13 +96,13 @@ public Grammar CreateGrammar() { return null; // resolve dependencies while loading and creating grammars - _enableAssemblyResolution = true; + _enableBrowsingForAssemblyResolution = true; try { var type = SelectedGrammarAssembly.GetType(SelectedGrammar.TypeName, true, true); return Activator.CreateInstance(type) as Grammar; } finally { - _enableAssemblyResolution = false; + _enableBrowsingForAssemblyResolution = false; } } @@ -123,22 +146,29 @@ private FileSystemWatcher CreateFileWatcher(string location) { if (args.ChangeType != WatcherChangeTypes.Changed) return; - // check if assembly was changed indeed to work around multiple FileSystemWatcher event firing - var cacheEntry = _cachedGrammarAssemblies[location]; - var fileInfo = new FileInfo(location); - if (cacheEntry.LastWriteTime == fileInfo.LastWriteTime && cacheEntry.FileSize == fileInfo.Length) - return; - - // clear cached assembly and save last file update time - cacheEntry.LastWriteTime = fileInfo.LastWriteTime; - cacheEntry.FileSize = fileInfo.Length; - cacheEntry.Assembly = null; - - // delay auto-refresh for safety reasons - ThreadPool.QueueUserWorkItem(_ => { - Thread.Sleep(_autoRefreshDelay); - OnAssemblyUpdated(location); - }); + lock (this) { + // check if assembly file was changed indeed since the last event + var cacheEntry = _cachedGrammarAssemblies[location]; + var fileInfo = new FileInfo(location); + if (cacheEntry.LastWriteTime == fileInfo.LastWriteTime && cacheEntry.FileSize == fileInfo.Length) + return; + + // reset cached assembly and save last file update time + cacheEntry.LastWriteTime = fileInfo.LastWriteTime; + cacheEntry.FileSize = fileInfo.Length; + cacheEntry.Assembly = null; + + // check if file update is already scheduled (work around multiple FileSystemWatcher event firing) + if (!cacheEntry.UpdateScheduled) { + cacheEntry.UpdateScheduled = true; + // delay auto-refresh to make sure the file is closed by the writer + ThreadPool.QueueUserWorkItem(_ => { + Thread.Sleep(_autoRefreshDelay); + cacheEntry.UpdateScheduled = false; + OnAssemblyUpdated(location); + }); + } + } }; watcher.EnableRaisingEvents = true; @@ -152,12 +182,20 @@ private void OnAssemblyUpdated(string location) { } public static Assembly LoadAssembly(string fileName) { + // normalize the filename + fileName = new FileInfo(fileName).FullName; // save assembly path for dependent assemblies probing var path = Path.GetDirectoryName(fileName); _probingPaths.Add(path); - // 1. Assembly.Load doesn't block the file - // 2. Assembly.Load doesn't check if the assembly is already loaded in the current AppDomain - return Assembly.Load(File.ReadAllBytes(fileName)); + // try to load assembly using the standard policy + var assembly = Assembly.LoadFrom(fileName); + // if the standard policy returned the old version, force reload + Assembly oldVersion; + if (_loadedAssembliesByLocation.TryGetValue(fileName, out oldVersion) && assembly == oldVersion) + assembly = Assembly.Load(File.ReadAllBytes(fileName)); + // cache the loaded assembly by its location + _loadedAssembliesByLocation[fileName] = assembly; + return assembly; } } } From 522926e12424ed7de2eecfb70c6e1e5975634770 Mon Sep 17 00:00:00 2001 From: Alexey Yakovlev Date: Fri, 9 Mar 2012 04:23:57 +0400 Subject: [PATCH 3/5] Minor bugfix. --- Irony.GrammarExplorer/GrammarLoader.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Irony.GrammarExplorer/GrammarLoader.cs b/Irony.GrammarExplorer/GrammarLoader.cs index e7dceb2..6723bbe 100644 --- a/Irony.GrammarExplorer/GrammarLoader.cs +++ b/Irony.GrammarExplorer/GrammarLoader.cs @@ -27,10 +27,10 @@ namespace Irony.GrammarExplorer { /// class GrammarLoader { private TimeSpan _autoRefreshDelay = TimeSpan.FromMilliseconds(1000); - private Dictionary _cachedGrammarAssemblies = new Dictionary(); private static HashSet _probingPaths = new HashSet(); - private static Dictionary _loadedAssembliesByLocation = new Dictionary(); + private Dictionary _cachedGrammarAssemblies = new Dictionary(); private static Dictionary _loadedAssembliesByNames = new Dictionary(); + private static Dictionary _loadedAssembliesByLocation = new Dictionary(); private static bool _enableBrowsingForAssemblyResolution = false; static GrammarLoader() { @@ -191,10 +191,12 @@ public static Assembly LoadAssembly(string fileName) { var assembly = Assembly.LoadFrom(fileName); // if the standard policy returned the old version, force reload Assembly oldVersion; - if (_loadedAssembliesByLocation.TryGetValue(fileName, out oldVersion) && assembly == oldVersion) - assembly = Assembly.Load(File.ReadAllBytes(fileName)); - // cache the loaded assembly by its location - _loadedAssembliesByLocation[fileName] = assembly; + if (_loadedAssembliesByLocation.TryGetValue(fileName, out oldVersion)) { + if (assembly == oldVersion) + assembly = Assembly.Load(File.ReadAllBytes(fileName)); + } + else // cache the loaded assembly by its location + _loadedAssembliesByLocation[fileName] = assembly; return assembly; } } From 4621232359a19a31eed8c32c20cee17e2f772a5d Mon Sep 17 00:00:00 2001 From: Alexey Yakovlev Date: Fri, 9 Mar 2012 05:02:17 +0400 Subject: [PATCH 4/5] Added Refresh button: allows refreshing currently selected grammar when auto-refresh checkbox is unchecked or the file system doesn't support change notifications. --- .../fmGrammarExplorer.Designer.cs | 879 ++++++++---------- Irony.GrammarExplorer/fmGrammarExplorer.cs | 115 +-- Irony.GrammarExplorer/fmGrammarExplorer.resx | 33 + 3 files changed, 503 insertions(+), 524 deletions(-) diff --git a/Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs b/Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs index b6f33f6..560f555 100644 --- a/Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs +++ b/Irony.GrammarExplorer/fmGrammarExplorer.Designer.cs @@ -26,14 +26,14 @@ protected override void Dispose(bool disposing) { private void InitializeComponent() { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(fmGrammarExplorer)); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle4 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle5 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle6 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle17 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle18 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle19 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle20 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle21 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle22 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle23 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle24 = new System.Windows.Forms.DataGridViewCellStyle(); this.tabGrammar = new System.Windows.Forms.TabControl(); this.pageTerminals = new System.Windows.Forms.TabPage(); this.txtTerms = new System.Windows.Forms.TextBox(); @@ -130,6 +130,7 @@ private void InitializeComponent() { this.label5 = new System.Windows.Forms.Label(); this.lblRunTime = new System.Windows.Forms.Label(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); + this.btnRefresh = new System.Windows.Forms.Button(); this.tabGrammar.SuspendLayout(); this.pageTerminals.SuspendLayout(); this.pageNonTerms.SuspendLayout(); @@ -158,291 +159,270 @@ private void InitializeComponent() { this.pageOutput.SuspendLayout(); this.pnlRuntimeInfo.SuspendLayout(); this.SuspendLayout(); - // + // // tabGrammar - // + // this.tabGrammar.Controls.Add(this.pageTerminals); this.tabGrammar.Controls.Add(this.pageNonTerms); this.tabGrammar.Controls.Add(this.pageParserStates); this.tabGrammar.Controls.Add(this.pageTest); this.tabGrammar.Dock = System.Windows.Forms.DockStyle.Fill; - this.tabGrammar.Location = new System.Drawing.Point(0, 36); - this.tabGrammar.Margin = new System.Windows.Forms.Padding(4); + this.tabGrammar.Location = new System.Drawing.Point(0, 29); this.tabGrammar.Name = "tabGrammar"; this.tabGrammar.SelectedIndex = 0; - this.tabGrammar.Size = new System.Drawing.Size(1472, 572); + this.tabGrammar.Size = new System.Drawing.Size(1022, 381); this.tabGrammar.TabIndex = 0; - // + // // pageTerminals - // + // this.pageTerminals.Controls.Add(this.txtTerms); - this.pageTerminals.Location = new System.Drawing.Point(4, 25); - this.pageTerminals.Margin = new System.Windows.Forms.Padding(4); + this.pageTerminals.Location = new System.Drawing.Point(4, 22); this.pageTerminals.Name = "pageTerminals"; - this.pageTerminals.Padding = new System.Windows.Forms.Padding(4); - this.pageTerminals.Size = new System.Drawing.Size(1464, 543); + this.pageTerminals.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageTerminals.Size = new System.Drawing.Size(1014, 355); this.pageTerminals.TabIndex = 5; this.pageTerminals.Text = "Terminals"; this.pageTerminals.UseVisualStyleBackColor = true; - // + // // txtTerms - // + // this.txtTerms.Dock = System.Windows.Forms.DockStyle.Fill; this.txtTerms.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.txtTerms.HideSelection = false; - this.txtTerms.Location = new System.Drawing.Point(4, 4); - this.txtTerms.Margin = new System.Windows.Forms.Padding(4); + this.txtTerms.Location = new System.Drawing.Point(3, 3); this.txtTerms.Multiline = true; this.txtTerms.Name = "txtTerms"; this.txtTerms.ReadOnly = true; this.txtTerms.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.txtTerms.Size = new System.Drawing.Size(1456, 535); + this.txtTerms.Size = new System.Drawing.Size(1008, 349); this.txtTerms.TabIndex = 2; - // + // // pageNonTerms - // + // this.pageNonTerms.Controls.Add(this.txtNonTerms); - this.pageNonTerms.Location = new System.Drawing.Point(4, 25); - this.pageNonTerms.Margin = new System.Windows.Forms.Padding(4); + this.pageNonTerms.Location = new System.Drawing.Point(4, 22); this.pageNonTerms.Name = "pageNonTerms"; - this.pageNonTerms.Padding = new System.Windows.Forms.Padding(4); - this.pageNonTerms.Size = new System.Drawing.Size(1464, 543); + this.pageNonTerms.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageNonTerms.Size = new System.Drawing.Size(1096, 439); this.pageNonTerms.TabIndex = 0; this.pageNonTerms.Text = "Non-Terminals"; this.pageNonTerms.UseVisualStyleBackColor = true; - // + // // txtNonTerms - // + // this.txtNonTerms.Dock = System.Windows.Forms.DockStyle.Fill; this.txtNonTerms.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.txtNonTerms.HideSelection = false; - this.txtNonTerms.Location = new System.Drawing.Point(4, 4); - this.txtNonTerms.Margin = new System.Windows.Forms.Padding(4); + this.txtNonTerms.Location = new System.Drawing.Point(3, 3); this.txtNonTerms.Multiline = true; this.txtNonTerms.Name = "txtNonTerms"; this.txtNonTerms.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.txtNonTerms.Size = new System.Drawing.Size(1456, 535); + this.txtNonTerms.Size = new System.Drawing.Size(1090, 433); this.txtNonTerms.TabIndex = 1; this.txtNonTerms.WordWrap = false; - // + // // pageParserStates - // + // this.pageParserStates.Controls.Add(this.txtParserStates); - this.pageParserStates.Location = new System.Drawing.Point(4, 25); - this.pageParserStates.Margin = new System.Windows.Forms.Padding(4); + this.pageParserStates.Location = new System.Drawing.Point(4, 22); this.pageParserStates.Name = "pageParserStates"; - this.pageParserStates.Padding = new System.Windows.Forms.Padding(4); - this.pageParserStates.Size = new System.Drawing.Size(1464, 543); + this.pageParserStates.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageParserStates.Size = new System.Drawing.Size(1096, 439); this.pageParserStates.TabIndex = 1; this.pageParserStates.Text = "Parser States"; this.pageParserStates.UseVisualStyleBackColor = true; - // + // // txtParserStates - // + // this.txtParserStates.Dock = System.Windows.Forms.DockStyle.Fill; this.txtParserStates.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.txtParserStates.HideSelection = false; - this.txtParserStates.Location = new System.Drawing.Point(4, 4); - this.txtParserStates.Margin = new System.Windows.Forms.Padding(4); + this.txtParserStates.Location = new System.Drawing.Point(3, 3); this.txtParserStates.Multiline = true; this.txtParserStates.Name = "txtParserStates"; this.txtParserStates.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.txtParserStates.Size = new System.Drawing.Size(1456, 535); + this.txtParserStates.Size = new System.Drawing.Size(1090, 433); this.txtParserStates.TabIndex = 2; this.txtParserStates.WordWrap = false; - // + // // pageTest - // + // this.pageTest.Controls.Add(this.txtSource); this.pageTest.Controls.Add(this.panel1); this.pageTest.Controls.Add(this.splitter3); this.pageTest.Controls.Add(this.tabOutput); - this.pageTest.Location = new System.Drawing.Point(4, 25); - this.pageTest.Margin = new System.Windows.Forms.Padding(4); + this.pageTest.Location = new System.Drawing.Point(4, 22); this.pageTest.Name = "pageTest"; - this.pageTest.Padding = new System.Windows.Forms.Padding(4); - this.pageTest.Size = new System.Drawing.Size(1464, 543); + this.pageTest.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageTest.Size = new System.Drawing.Size(1096, 439); this.pageTest.TabIndex = 4; this.pageTest.Text = "Test"; this.pageTest.UseVisualStyleBackColor = true; - // + // // txtSource - // + // this.txtSource.Dock = System.Windows.Forms.DockStyle.Fill; this.txtSource.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.txtSource.HideSelection = false; - this.txtSource.Location = new System.Drawing.Point(4, 41); - this.txtSource.Margin = new System.Windows.Forms.Padding(4); + this.txtSource.Location = new System.Drawing.Point(3, 33); this.txtSource.Name = "txtSource"; - this.txtSource.Size = new System.Drawing.Size(981, 498); + this.txtSource.Size = new System.Drawing.Size(734, 403); this.txtSource.TabIndex = 22; this.txtSource.Text = ""; this.txtSource.TextChanged += new System.EventHandler(this.txtSource_TextChanged); - // + // // panel1 - // + // this.panel1.Controls.Add(this.btnLocate); this.panel1.Controls.Add(this.chkDisableHili); this.panel1.Controls.Add(this.btnRun); this.panel1.Controls.Add(this.btnFileOpen); this.panel1.Controls.Add(this.btnParse); this.panel1.Dock = System.Windows.Forms.DockStyle.Top; - this.panel1.Location = new System.Drawing.Point(4, 4); - this.panel1.Margin = new System.Windows.Forms.Padding(4); + this.panel1.Location = new System.Drawing.Point(3, 3); this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(981, 37); + this.panel1.Size = new System.Drawing.Size(734, 30); this.panel1.TabIndex = 2; - // + // // btnLocate - // + // this.btnLocate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnLocate.Location = new System.Drawing.Point(883, 4); - this.btnLocate.Margin = new System.Windows.Forms.Padding(4); + this.btnLocate.Location = new System.Drawing.Point(660, 3); this.btnLocate.Name = "btnLocate"; - this.btnLocate.Size = new System.Drawing.Size(87, 28); + this.btnLocate.Size = new System.Drawing.Size(65, 23); this.btnLocate.TabIndex = 10; this.btnLocate.Text = "Locate >>"; this.toolTip.SetToolTip(this.btnLocate, "Locate the source position in parse/Ast tree. "); this.btnLocate.UseVisualStyleBackColor = true; this.btnLocate.Click += new System.EventHandler(this.btnLocate_Click); - // + // // chkDisableHili - // + // this.chkDisableHili.AutoSize = true; - this.chkDisableHili.Location = new System.Drawing.Point(7, 9); - this.chkDisableHili.Margin = new System.Windows.Forms.Padding(4); + this.chkDisableHili.Location = new System.Drawing.Point(5, 7); this.chkDisableHili.Name = "chkDisableHili"; - this.chkDisableHili.Size = new System.Drawing.Size(197, 21); + this.chkDisableHili.Size = new System.Drawing.Size(150, 17); this.chkDisableHili.TabIndex = 9; this.chkDisableHili.Text = "Disable syntax highlighting"; this.chkDisableHili.UseVisualStyleBackColor = true; this.chkDisableHili.CheckedChanged += new System.EventHandler(this.chkDisableHili_CheckedChanged); - // + // // btnRun - // + // this.btnRun.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnRun.Location = new System.Drawing.Point(787, 4); - this.btnRun.Margin = new System.Windows.Forms.Padding(4); + this.btnRun.Location = new System.Drawing.Point(588, 3); this.btnRun.Name = "btnRun"; - this.btnRun.Size = new System.Drawing.Size(87, 28); + this.btnRun.Size = new System.Drawing.Size(65, 23); this.btnRun.TabIndex = 7; this.btnRun.Text = "Run"; this.toolTip.SetToolTip(this.btnRun, "Run the source sample"); this.btnRun.UseVisualStyleBackColor = true; this.btnRun.Click += new System.EventHandler(this.btnRun_Click); - // + // // btnFileOpen - // + // this.btnFileOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnFileOpen.Location = new System.Drawing.Point(595, 4); - this.btnFileOpen.Margin = new System.Windows.Forms.Padding(4); + this.btnFileOpen.Location = new System.Drawing.Point(444, 3); this.btnFileOpen.Name = "btnFileOpen"; - this.btnFileOpen.Size = new System.Drawing.Size(87, 28); + this.btnFileOpen.Size = new System.Drawing.Size(65, 23); this.btnFileOpen.TabIndex = 6; this.btnFileOpen.Text = "Load ..."; this.toolTip.SetToolTip(this.btnFileOpen, "Load a source sample..."); this.btnFileOpen.UseVisualStyleBackColor = true; this.btnFileOpen.Click += new System.EventHandler(this.btnFileOpen_Click); - // + // // btnParse - // + // this.btnParse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.btnParse.Location = new System.Drawing.Point(689, 4); - this.btnParse.Margin = new System.Windows.Forms.Padding(4); + this.btnParse.Location = new System.Drawing.Point(515, 3); this.btnParse.Name = "btnParse"; - this.btnParse.Size = new System.Drawing.Size(89, 28); + this.btnParse.Size = new System.Drawing.Size(67, 23); this.btnParse.TabIndex = 1; this.btnParse.Text = "Parse"; this.toolTip.SetToolTip(this.btnParse, "Parse source sample"); this.btnParse.UseVisualStyleBackColor = true; this.btnParse.Click += new System.EventHandler(this.btnParse_Click); - // + // // splitter3 - // + // this.splitter3.Dock = System.Windows.Forms.DockStyle.Right; - this.splitter3.Location = new System.Drawing.Point(985, 4); - this.splitter3.Margin = new System.Windows.Forms.Padding(4); + this.splitter3.Location = new System.Drawing.Point(737, 3); this.splitter3.Name = "splitter3"; - this.splitter3.Size = new System.Drawing.Size(8, 535); + this.splitter3.Size = new System.Drawing.Size(6, 433); this.splitter3.TabIndex = 14; this.splitter3.TabStop = false; - // + // // tabOutput - // + // this.tabOutput.Controls.Add(this.pageSyntaxTree); this.tabOutput.Controls.Add(this.pageAst); this.tabOutput.Dock = System.Windows.Forms.DockStyle.Right; - this.tabOutput.Location = new System.Drawing.Point(993, 4); - this.tabOutput.Margin = new System.Windows.Forms.Padding(4); + this.tabOutput.Location = new System.Drawing.Point(743, 3); this.tabOutput.Name = "tabOutput"; this.tabOutput.SelectedIndex = 0; - this.tabOutput.Size = new System.Drawing.Size(467, 535); + this.tabOutput.Size = new System.Drawing.Size(350, 433); this.tabOutput.TabIndex = 13; - // + // // pageSyntaxTree - // + // this.pageSyntaxTree.Controls.Add(this.tvParseTree); this.pageSyntaxTree.ForeColor = System.Drawing.SystemColors.ControlText; - this.pageSyntaxTree.Location = new System.Drawing.Point(4, 25); - this.pageSyntaxTree.Margin = new System.Windows.Forms.Padding(4); + this.pageSyntaxTree.Location = new System.Drawing.Point(4, 22); this.pageSyntaxTree.Name = "pageSyntaxTree"; - this.pageSyntaxTree.Padding = new System.Windows.Forms.Padding(4); - this.pageSyntaxTree.Size = new System.Drawing.Size(459, 506); + this.pageSyntaxTree.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageSyntaxTree.Size = new System.Drawing.Size(342, 407); this.pageSyntaxTree.TabIndex = 1; this.pageSyntaxTree.Text = "Parse Tree"; this.pageSyntaxTree.UseVisualStyleBackColor = true; - // + // // tvParseTree - // + // this.tvParseTree.Dock = System.Windows.Forms.DockStyle.Fill; this.tvParseTree.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.tvParseTree.HideSelection = false; this.tvParseTree.Indent = 16; - this.tvParseTree.Location = new System.Drawing.Point(4, 4); - this.tvParseTree.Margin = new System.Windows.Forms.Padding(4); + this.tvParseTree.Location = new System.Drawing.Point(3, 3); this.tvParseTree.Name = "tvParseTree"; - this.tvParseTree.Size = new System.Drawing.Size(451, 498); + this.tvParseTree.Size = new System.Drawing.Size(336, 401); this.tvParseTree.TabIndex = 0; this.tvParseTree.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvParseTree_AfterSelect); - // + // // pageAst - // + // this.pageAst.Controls.Add(this.tvAst); - this.pageAst.Location = new System.Drawing.Point(4, 25); - this.pageAst.Margin = new System.Windows.Forms.Padding(4); + this.pageAst.Location = new System.Drawing.Point(4, 22); this.pageAst.Name = "pageAst"; - this.pageAst.Padding = new System.Windows.Forms.Padding(4); - this.pageAst.Size = new System.Drawing.Size(459, 506); + this.pageAst.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageAst.Size = new System.Drawing.Size(342, 409); this.pageAst.TabIndex = 0; this.pageAst.Text = "AST"; this.pageAst.UseVisualStyleBackColor = true; - // + // // tvAst - // + // this.tvAst.Dock = System.Windows.Forms.DockStyle.Fill; this.tvAst.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.tvAst.HideSelection = false; this.tvAst.Indent = 16; - this.tvAst.Location = new System.Drawing.Point(4, 4); - this.tvAst.Margin = new System.Windows.Forms.Padding(4); + this.tvAst.Location = new System.Drawing.Point(3, 3); this.tvAst.Name = "tvAst"; - this.tvAst.Size = new System.Drawing.Size(451, 498); + this.tvAst.Size = new System.Drawing.Size(336, 403); this.tvAst.TabIndex = 1; this.tvAst.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.tvAst_AfterSelect); - // + // // chkParserTrace - // + // this.chkParserTrace.AutoSize = true; - this.chkParserTrace.Location = new System.Drawing.Point(4, 4); - this.chkParserTrace.Margin = new System.Windows.Forms.Padding(4); + this.chkParserTrace.Location = new System.Drawing.Point(3, 3); this.chkParserTrace.Name = "chkParserTrace"; - this.chkParserTrace.Size = new System.Drawing.Size(115, 21); + this.chkParserTrace.Size = new System.Drawing.Size(90, 17); this.chkParserTrace.TabIndex = 0; this.chkParserTrace.Text = "Enable Trace"; this.chkParserTrace.UseVisualStyleBackColor = true; - // + // // pnlLang - // + // + this.pnlLang.Controls.Add(this.btnRefresh); this.pnlLang.Controls.Add(this.chkAutoRefresh); this.pnlLang.Controls.Add(this.btnManageGrammars); this.pnlLang.Controls.Add(this.lblSearchError); @@ -452,94 +432,91 @@ private void InitializeComponent() { this.pnlLang.Controls.Add(this.cboGrammars); this.pnlLang.Dock = System.Windows.Forms.DockStyle.Top; this.pnlLang.Location = new System.Drawing.Point(0, 0); - this.pnlLang.Margin = new System.Windows.Forms.Padding(4); this.pnlLang.Name = "pnlLang"; - this.pnlLang.Size = new System.Drawing.Size(1472, 36); + this.pnlLang.Size = new System.Drawing.Size(1022, 29); this.pnlLang.TabIndex = 13; - // + // // chkAutoRefresh - // + // this.chkAutoRefresh.AutoSize = true; this.chkAutoRefresh.Checked = true; this.chkAutoRefresh.CheckState = System.Windows.Forms.CheckState.Checked; - this.chkAutoRefresh.Location = new System.Drawing.Point(459, 7); - this.chkAutoRefresh.Margin = new System.Windows.Forms.Padding(4); + this.chkAutoRefresh.Location = new System.Drawing.Point(401, 7); this.chkAutoRefresh.Name = "chkAutoRefresh"; - this.chkAutoRefresh.Size = new System.Drawing.Size(109, 21); + this.chkAutoRefresh.Size = new System.Drawing.Size(83, 17); this.chkAutoRefresh.TabIndex = 13; this.chkAutoRefresh.Text = "Auto-refresh"; this.toolTip.SetToolTip(this.chkAutoRefresh, resources.GetString("chkAutoRefresh.ToolTip")); this.chkAutoRefresh.UseVisualStyleBackColor = true; - // + // // btnManageGrammars - // - this.btnManageGrammars.Location = new System.Drawing.Point(408, 2); - this.btnManageGrammars.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + // + this.btnManageGrammars.Location = new System.Drawing.Point(306, 2); + this.btnManageGrammars.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.btnManageGrammars.Name = "btnManageGrammars"; - this.btnManageGrammars.Size = new System.Drawing.Size(37, 30); + this.btnManageGrammars.Size = new System.Drawing.Size(28, 24); this.btnManageGrammars.TabIndex = 12; this.btnManageGrammars.Text = "..."; this.btnManageGrammars.UseVisualStyleBackColor = true; this.btnManageGrammars.Click += new System.EventHandler(this.btnManageGrammars_Click); - // + // // lblSearchError - // + // this.lblSearchError.AutoSize = true; this.lblSearchError.ForeColor = System.Drawing.Color.Red; - this.lblSearchError.Location = new System.Drawing.Point(975, 11); + this.lblSearchError.Location = new System.Drawing.Point(731, 9); + this.lblSearchError.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.lblSearchError.Name = "lblSearchError"; - this.lblSearchError.Size = new System.Drawing.Size(70, 17); + this.lblSearchError.Size = new System.Drawing.Size(54, 13); this.lblSearchError.TabIndex = 11; this.lblSearchError.Text = "Not found"; this.lblSearchError.Visible = false; - // + // // btnSearch - // - this.btnSearch.Location = new System.Drawing.Point(896, 5); - this.btnSearch.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + // + this.btnSearch.Location = new System.Drawing.Point(672, 2); + this.btnSearch.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.btnSearch.Name = "btnSearch"; - this.btnSearch.Size = new System.Drawing.Size(73, 28); + this.btnSearch.Size = new System.Drawing.Size(55, 24); this.btnSearch.TabIndex = 10; this.btnSearch.Text = "Find"; this.btnSearch.UseVisualStyleBackColor = true; this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click); - // + // // txtSearch - // + // this.txtSearch.AcceptsReturn = true; this.txtSearch.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::Irony.GrammarExplorer.Properties.Settings.Default, "SearchPattern", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.txtSearch.Location = new System.Drawing.Point(727, 5); - this.txtSearch.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); + this.txtSearch.Location = new System.Drawing.Point(545, 4); + this.txtSearch.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.txtSearch.Name = "txtSearch"; - this.txtSearch.Size = new System.Drawing.Size(163, 22); + this.txtSearch.Size = new System.Drawing.Size(123, 20); this.txtSearch.TabIndex = 8; this.txtSearch.Text = global::Irony.GrammarExplorer.Properties.Settings.Default.SearchPattern; this.txtSearch.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtSearch_KeyPress); - // + // // label2 - // + // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(13, 7); - this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label2.Location = new System.Drawing.Point(10, 6); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(71, 17); + this.label2.Size = new System.Drawing.Size(52, 13); this.label2.TabIndex = 4; this.label2.Text = "Grammar:"; - // + // // cboGrammars - // + // this.cboGrammars.ContextMenuStrip = this.menuGrammars; this.cboGrammars.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.cboGrammars.FormattingEnabled = true; - this.cboGrammars.Location = new System.Drawing.Point(89, 4); - this.cboGrammars.Margin = new System.Windows.Forms.Padding(4); + this.cboGrammars.Location = new System.Drawing.Point(67, 3); this.cboGrammars.Name = "cboGrammars"; - this.cboGrammars.Size = new System.Drawing.Size(311, 24); + this.cboGrammars.Size = new System.Drawing.Size(234, 21); this.cboGrammars.TabIndex = 3; this.cboGrammars.SelectedIndexChanged += new System.EventHandler(this.cboGrammars_SelectedIndexChanged); - // + // // menuGrammars - // + // this.menuGrammars.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.miAdd, this.miRemove, @@ -547,75 +524,72 @@ private void InitializeComponent() { this.menuGrammars.Name = "menuGrammars"; this.menuGrammars.Size = new System.Drawing.Size(164, 70); this.menuGrammars.Opening += new System.ComponentModel.CancelEventHandler(this.menuGrammars_Opening); - // + // // miAdd - // + // this.miAdd.Name = "miAdd"; this.miAdd.Size = new System.Drawing.Size(163, 22); this.miAdd.Text = "Add grammar..."; this.miAdd.Click += new System.EventHandler(this.miAdd_Click); - // + // // miRemove - // + // this.miRemove.Name = "miRemove"; this.miRemove.Size = new System.Drawing.Size(163, 22); this.miRemove.Text = "Remove selected"; this.miRemove.Click += new System.EventHandler(this.miRemove_Click); - // + // // miRemoveAll - // + // this.miRemoveAll.Name = "miRemoveAll"; this.miRemoveAll.Size = new System.Drawing.Size(163, 22); this.miRemoveAll.Text = "Remove all"; this.miRemoveAll.Click += new System.EventHandler(this.miRemoveAll_Click); - // + // // dlgSelectAssembly - // + // this.dlgSelectAssembly.DefaultExt = "dll"; this.dlgSelectAssembly.Filter = "DLL files|*.dll"; this.dlgSelectAssembly.Title = "Select Grammar Assembly "; - // + // // splitBottom - // + // this.splitBottom.BackColor = System.Drawing.SystemColors.Control; this.splitBottom.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.splitBottom.Dock = System.Windows.Forms.DockStyle.Bottom; - this.splitBottom.Location = new System.Drawing.Point(0, 608); - this.splitBottom.Margin = new System.Windows.Forms.Padding(4); + this.splitBottom.Location = new System.Drawing.Point(0, 410); this.splitBottom.Name = "splitBottom"; - this.splitBottom.Size = new System.Drawing.Size(1472, 6); + this.splitBottom.Size = new System.Drawing.Size(1022, 6); this.splitBottom.TabIndex = 22; this.splitBottom.TabStop = false; - // + // // tabBottom - // + // this.tabBottom.Controls.Add(this.pageLanguage); this.tabBottom.Controls.Add(this.pageGrammarErrors); this.tabBottom.Controls.Add(this.pageParserOutput); this.tabBottom.Controls.Add(this.pageParserTrace); this.tabBottom.Controls.Add(this.pageOutput); this.tabBottom.Dock = System.Windows.Forms.DockStyle.Bottom; - this.tabBottom.Location = new System.Drawing.Point(0, 614); - this.tabBottom.Margin = new System.Windows.Forms.Padding(4); + this.tabBottom.Location = new System.Drawing.Point(0, 416); this.tabBottom.Name = "tabBottom"; this.tabBottom.SelectedIndex = 0; - this.tabBottom.Size = new System.Drawing.Size(1472, 230); + this.tabBottom.Size = new System.Drawing.Size(1022, 187); this.tabBottom.TabIndex = 0; - // + // // pageLanguage - // + // this.pageLanguage.Controls.Add(this.grpLanguageInfo); - this.pageLanguage.Location = new System.Drawing.Point(4, 25); - this.pageLanguage.Margin = new System.Windows.Forms.Padding(4); + this.pageLanguage.Location = new System.Drawing.Point(4, 22); this.pageLanguage.Name = "pageLanguage"; - this.pageLanguage.Padding = new System.Windows.Forms.Padding(4); - this.pageLanguage.Size = new System.Drawing.Size(1464, 201); + this.pageLanguage.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageLanguage.Size = new System.Drawing.Size(1014, 161); this.pageLanguage.TabIndex = 1; this.pageLanguage.Text = "Grammar Info"; this.pageLanguage.UseVisualStyleBackColor = true; - // + // // grpLanguageInfo - // + // this.grpLanguageInfo.Controls.Add(this.label8); this.grpLanguageInfo.Controls.Add(this.lblParserStateCount); this.grpLanguageInfo.Controls.Add(this.lblLanguageDescr); @@ -629,147 +603,132 @@ private void InitializeComponent() { this.grpLanguageInfo.Controls.Add(this.label6); this.grpLanguageInfo.Controls.Add(this.lblParserConstrTime); this.grpLanguageInfo.Dock = System.Windows.Forms.DockStyle.Fill; - this.grpLanguageInfo.Location = new System.Drawing.Point(4, 4); - this.grpLanguageInfo.Margin = new System.Windows.Forms.Padding(4); + this.grpLanguageInfo.Location = new System.Drawing.Point(3, 3); this.grpLanguageInfo.Name = "grpLanguageInfo"; - this.grpLanguageInfo.Padding = new System.Windows.Forms.Padding(4); - this.grpLanguageInfo.Size = new System.Drawing.Size(1456, 193); + this.grpLanguageInfo.Size = new System.Drawing.Size(1008, 155); this.grpLanguageInfo.TabIndex = 3; this.grpLanguageInfo.TabStop = false; - // + // // label8 - // + // this.label8.AutoSize = true; - this.label8.Location = new System.Drawing.Point(8, 139); - this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label8.Location = new System.Drawing.Point(6, 113); this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(128, 17); + this.label8.Size = new System.Drawing.Size(96, 13); this.label8.TabIndex = 26; this.label8.Text = "Parser state count:"; - // + // // lblParserStateCount - // + // this.lblParserStateCount.AutoSize = true; - this.lblParserStateCount.Location = new System.Drawing.Point(223, 139); - this.lblParserStateCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParserStateCount.Location = new System.Drawing.Point(167, 113); this.lblParserStateCount.Name = "lblParserStateCount"; - this.lblParserStateCount.Size = new System.Drawing.Size(16, 17); + this.lblParserStateCount.Size = new System.Drawing.Size(13, 13); this.lblParserStateCount.TabIndex = 25; this.lblParserStateCount.Text = "0"; - // + // // lblLanguageDescr - // - this.lblLanguageDescr.Location = new System.Drawing.Point(143, 47); - this.lblLanguageDescr.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + // + this.lblLanguageDescr.Location = new System.Drawing.Point(107, 38); this.lblLanguageDescr.Name = "lblLanguageDescr"; - this.lblLanguageDescr.Size = new System.Drawing.Size(817, 27); + this.lblLanguageDescr.Size = new System.Drawing.Size(613, 22); this.lblLanguageDescr.TabIndex = 24; this.lblLanguageDescr.Text = "(description)"; - // + // // txtGrammarComments - // + // this.txtGrammarComments.BackColor = System.Drawing.SystemColors.Window; this.txtGrammarComments.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.txtGrammarComments.Location = new System.Drawing.Point(148, 78); - this.txtGrammarComments.Margin = new System.Windows.Forms.Padding(4); + this.txtGrammarComments.Location = new System.Drawing.Point(111, 63); this.txtGrammarComments.Multiline = true; this.txtGrammarComments.Name = "txtGrammarComments"; this.txtGrammarComments.ReadOnly = true; - this.txtGrammarComments.Size = new System.Drawing.Size(812, 58); + this.txtGrammarComments.Size = new System.Drawing.Size(609, 47); this.txtGrammarComments.TabIndex = 23; - // + // // label11 - // + // this.label11.AutoSize = true; - this.label11.Location = new System.Drawing.Point(8, 75); - this.label11.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label11.Location = new System.Drawing.Point(6, 61); this.label11.Name = "label11"; - this.label11.Size = new System.Drawing.Size(134, 17); + this.label11.Size = new System.Drawing.Size(99, 13); this.label11.TabIndex = 22; this.label11.Text = "Grammar Comment:"; - // + // // label9 - // + // this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(8, 47); - this.label9.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label9.Location = new System.Drawing.Point(6, 38); this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(83, 17); + this.label9.Size = new System.Drawing.Size(63, 13); this.label9.TabIndex = 20; this.label9.Text = "Description:"; - // + // // lblLanguageVersion - // - this.lblLanguageVersion.Location = new System.Drawing.Point(371, 20); - this.lblLanguageVersion.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + // + this.lblLanguageVersion.Location = new System.Drawing.Point(278, 16); this.lblLanguageVersion.Name = "lblLanguageVersion"; - this.lblLanguageVersion.Size = new System.Drawing.Size(107, 21); + this.lblLanguageVersion.Size = new System.Drawing.Size(80, 17); this.lblLanguageVersion.TabIndex = 19; this.lblLanguageVersion.Text = "(Version)"; - // + // // label10 - // + // this.label10.AutoSize = true; - this.label10.Location = new System.Drawing.Point(303, 20); - this.label10.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label10.Location = new System.Drawing.Point(227, 16); this.label10.Name = "label10"; - this.label10.Size = new System.Drawing.Size(60, 17); + this.label10.Size = new System.Drawing.Size(45, 13); this.label10.TabIndex = 18; this.label10.Text = "Version:"; - // + // // lblLanguage - // - this.lblLanguage.Location = new System.Drawing.Point(143, 20); - this.lblLanguage.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + // + this.lblLanguage.Location = new System.Drawing.Point(107, 16); this.lblLanguage.Name = "lblLanguage"; - this.lblLanguage.Size = new System.Drawing.Size(307, 21); + this.lblLanguage.Size = new System.Drawing.Size(230, 17); this.lblLanguage.TabIndex = 17; this.lblLanguage.Text = "(Language name)"; - // + // // label4 - // + // this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(8, 20); - this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label4.Location = new System.Drawing.Point(6, 16); this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(76, 17); + this.label4.Size = new System.Drawing.Size(58, 13); this.label4.TabIndex = 16; this.label4.Text = "Language:"; - // + // // label6 - // + // this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(8, 162); - this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label6.Location = new System.Drawing.Point(6, 132); this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(191, 17); + this.label6.Size = new System.Drawing.Size(142, 13); this.label6.TabIndex = 15; this.label6.Text = "Parser construction time, ms:"; - // + // // lblParserConstrTime - // + // this.lblParserConstrTime.AutoSize = true; - this.lblParserConstrTime.Location = new System.Drawing.Point(223, 162); - this.lblParserConstrTime.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParserConstrTime.Location = new System.Drawing.Point(167, 132); this.lblParserConstrTime.Name = "lblParserConstrTime"; - this.lblParserConstrTime.Size = new System.Drawing.Size(16, 17); + this.lblParserConstrTime.Size = new System.Drawing.Size(13, 13); this.lblParserConstrTime.TabIndex = 14; this.lblParserConstrTime.Text = "0"; - // + // // pageGrammarErrors - // + // this.pageGrammarErrors.Controls.Add(this.gridGrammarErrors); - this.pageGrammarErrors.Location = new System.Drawing.Point(4, 25); - this.pageGrammarErrors.Margin = new System.Windows.Forms.Padding(4); + this.pageGrammarErrors.Location = new System.Drawing.Point(4, 22); this.pageGrammarErrors.Name = "pageGrammarErrors"; - this.pageGrammarErrors.Padding = new System.Windows.Forms.Padding(4); - this.pageGrammarErrors.Size = new System.Drawing.Size(1464, 201); + this.pageGrammarErrors.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageGrammarErrors.Size = new System.Drawing.Size(1096, 161); this.pageGrammarErrors.TabIndex = 4; this.pageGrammarErrors.Text = "Grammar Errors"; this.pageGrammarErrors.UseVisualStyleBackColor = true; - // + // // gridGrammarErrors - // + // this.gridGrammarErrors.AllowUserToAddRows = false; this.gridGrammarErrors.AllowUserToDeleteRows = false; this.gridGrammarErrors.ColumnHeadersHeight = 24; @@ -779,79 +738,75 @@ private void InitializeComponent() { this.dataGridViewTextBoxColumn5, this.dataGridViewTextBoxColumn6}); this.gridGrammarErrors.Dock = System.Windows.Forms.DockStyle.Fill; - this.gridGrammarErrors.Location = new System.Drawing.Point(4, 4); - this.gridGrammarErrors.Margin = new System.Windows.Forms.Padding(4); + this.gridGrammarErrors.Location = new System.Drawing.Point(3, 3); this.gridGrammarErrors.MultiSelect = false; this.gridGrammarErrors.Name = "gridGrammarErrors"; this.gridGrammarErrors.ReadOnly = true; this.gridGrammarErrors.RowHeadersVisible = false; this.gridGrammarErrors.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; - this.gridGrammarErrors.Size = new System.Drawing.Size(1453, 191); + this.gridGrammarErrors.Size = new System.Drawing.Size(1090, 155); this.gridGrammarErrors.TabIndex = 3; this.gridGrammarErrors.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridGrammarErrors_CellDoubleClick); - // + // // dataGridViewTextBoxColumn2 - // - dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - this.dataGridViewTextBoxColumn2.DefaultCellStyle = dataGridViewCellStyle1; + // + dataGridViewCellStyle17.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn2.DefaultCellStyle = dataGridViewCellStyle17; this.dataGridViewTextBoxColumn2.HeaderText = "Error Level"; this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2"; this.dataGridViewTextBoxColumn2.ReadOnly = true; this.dataGridViewTextBoxColumn2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.dataGridViewTextBoxColumn2.ToolTipText = "Double-click grid cell to locate in source code"; - // + // // dataGridViewTextBoxColumn5 - // - dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True; - this.dataGridViewTextBoxColumn5.DefaultCellStyle = dataGridViewCellStyle2; + // + dataGridViewCellStyle18.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dataGridViewTextBoxColumn5.DefaultCellStyle = dataGridViewCellStyle18; this.dataGridViewTextBoxColumn5.HeaderText = "Description"; this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5"; this.dataGridViewTextBoxColumn5.ReadOnly = true; this.dataGridViewTextBoxColumn5.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.dataGridViewTextBoxColumn5.Width = 800; - // + // // dataGridViewTextBoxColumn6 - // + // this.dataGridViewTextBoxColumn6.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; this.dataGridViewTextBoxColumn6.DataPropertyName = "State"; - dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - this.dataGridViewTextBoxColumn6.DefaultCellStyle = dataGridViewCellStyle3; + dataGridViewCellStyle19.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn6.DefaultCellStyle = dataGridViewCellStyle19; this.dataGridViewTextBoxColumn6.HeaderText = "Parser State"; this.dataGridViewTextBoxColumn6.Name = "dataGridViewTextBoxColumn6"; this.dataGridViewTextBoxColumn6.ReadOnly = true; this.dataGridViewTextBoxColumn6.Resizable = System.Windows.Forms.DataGridViewTriState.True; this.dataGridViewTextBoxColumn6.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.dataGridViewTextBoxColumn6.ToolTipText = "Double-click grid cell to navigate to state details"; - this.dataGridViewTextBoxColumn6.Width = 93; - // + this.dataGridViewTextBoxColumn6.Width = 71; + // // pageParserOutput - // + // this.pageParserOutput.Controls.Add(this.groupBox1); this.pageParserOutput.Controls.Add(this.grpCompileInfo); - this.pageParserOutput.Location = new System.Drawing.Point(4, 25); - this.pageParserOutput.Margin = new System.Windows.Forms.Padding(4); + this.pageParserOutput.Location = new System.Drawing.Point(4, 22); this.pageParserOutput.Name = "pageParserOutput"; - this.pageParserOutput.Padding = new System.Windows.Forms.Padding(4); - this.pageParserOutput.Size = new System.Drawing.Size(1464, 201); + this.pageParserOutput.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageParserOutput.Size = new System.Drawing.Size(1096, 161); this.pageParserOutput.TabIndex = 2; this.pageParserOutput.Text = "Parser Output"; this.pageParserOutput.UseVisualStyleBackColor = true; - // + // // groupBox1 - // + // this.groupBox1.Controls.Add(this.gridCompileErrors); this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill; - this.groupBox1.Location = new System.Drawing.Point(211, 4); - this.groupBox1.Margin = new System.Windows.Forms.Padding(4); + this.groupBox1.Location = new System.Drawing.Point(158, 3); this.groupBox1.Name = "groupBox1"; - this.groupBox1.Padding = new System.Windows.Forms.Padding(4); - this.groupBox1.Size = new System.Drawing.Size(1249, 193); + this.groupBox1.Size = new System.Drawing.Size(935, 155); this.groupBox1.TabIndex = 3; this.groupBox1.TabStop = false; this.groupBox1.Text = "Compile Errors"; - // + // // gridCompileErrors - // + // this.gridCompileErrors.AllowUserToAddRows = false; this.gridCompileErrors.AllowUserToDeleteRows = false; this.gridCompileErrors.ColumnHeadersHeight = 24; @@ -861,55 +816,54 @@ private void InitializeComponent() { this.dataGridViewTextBoxColumn4, this.dataGridViewTextBoxColumn1}); this.gridCompileErrors.Dock = System.Windows.Forms.DockStyle.Fill; - this.gridCompileErrors.Location = new System.Drawing.Point(4, 19); - this.gridCompileErrors.Margin = new System.Windows.Forms.Padding(4); + this.gridCompileErrors.Location = new System.Drawing.Point(3, 16); this.gridCompileErrors.MultiSelect = false; this.gridCompileErrors.Name = "gridCompileErrors"; this.gridCompileErrors.ReadOnly = true; this.gridCompileErrors.RowHeadersVisible = false; this.gridCompileErrors.RowTemplate.Height = 24; this.gridCompileErrors.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; - this.gridCompileErrors.Size = new System.Drawing.Size(1241, 170); + this.gridCompileErrors.Size = new System.Drawing.Size(929, 136); this.gridCompileErrors.TabIndex = 2; this.gridCompileErrors.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridCompileErrors_CellDoubleClick); - // + // // dataGridViewTextBoxColumn3 - // - dataGridViewCellStyle4.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - this.dataGridViewTextBoxColumn3.DefaultCellStyle = dataGridViewCellStyle4; + // + dataGridViewCellStyle20.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn3.DefaultCellStyle = dataGridViewCellStyle20; this.dataGridViewTextBoxColumn3.HeaderText = "L, C"; this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3"; this.dataGridViewTextBoxColumn3.ReadOnly = true; this.dataGridViewTextBoxColumn3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.dataGridViewTextBoxColumn3.ToolTipText = "Double-click grid cell to locate in source code"; this.dataGridViewTextBoxColumn3.Width = 50; - // + // // dataGridViewTextBoxColumn4 - // - dataGridViewCellStyle5.WrapMode = System.Windows.Forms.DataGridViewTriState.True; - this.dataGridViewTextBoxColumn4.DefaultCellStyle = dataGridViewCellStyle5; + // + dataGridViewCellStyle21.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.dataGridViewTextBoxColumn4.DefaultCellStyle = dataGridViewCellStyle21; this.dataGridViewTextBoxColumn4.HeaderText = "Error Message"; this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4"; this.dataGridViewTextBoxColumn4.ReadOnly = true; this.dataGridViewTextBoxColumn4.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.dataGridViewTextBoxColumn4.Width = 1000; - // + // // dataGridViewTextBoxColumn1 - // + // this.dataGridViewTextBoxColumn1.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCells; this.dataGridViewTextBoxColumn1.DataPropertyName = "State"; - dataGridViewCellStyle6.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - this.dataGridViewTextBoxColumn1.DefaultCellStyle = dataGridViewCellStyle6; + dataGridViewCellStyle22.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.dataGridViewTextBoxColumn1.DefaultCellStyle = dataGridViewCellStyle22; this.dataGridViewTextBoxColumn1.HeaderText = "Parser State"; this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1"; this.dataGridViewTextBoxColumn1.ReadOnly = true; this.dataGridViewTextBoxColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.True; this.dataGridViewTextBoxColumn1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.dataGridViewTextBoxColumn1.ToolTipText = "Double-click grid cell to navigate to state details"; - this.dataGridViewTextBoxColumn1.Width = 93; - // + this.dataGridViewTextBoxColumn1.Width = 71; + // // grpCompileInfo - // + // this.grpCompileInfo.Controls.Add(this.label12); this.grpCompileInfo.Controls.Add(this.lblParseErrorCount); this.grpCompileInfo.Controls.Add(this.label1); @@ -919,125 +873,112 @@ private void InitializeComponent() { this.grpCompileInfo.Controls.Add(this.label3); this.grpCompileInfo.Controls.Add(this.lblSrcTokenCount); this.grpCompileInfo.Dock = System.Windows.Forms.DockStyle.Left; - this.grpCompileInfo.Location = new System.Drawing.Point(4, 4); - this.grpCompileInfo.Margin = new System.Windows.Forms.Padding(4); + this.grpCompileInfo.Location = new System.Drawing.Point(3, 3); this.grpCompileInfo.Name = "grpCompileInfo"; - this.grpCompileInfo.Padding = new System.Windows.Forms.Padding(4); - this.grpCompileInfo.Size = new System.Drawing.Size(207, 193); + this.grpCompileInfo.Size = new System.Drawing.Size(155, 155); this.grpCompileInfo.TabIndex = 5; this.grpCompileInfo.TabStop = false; this.grpCompileInfo.Text = "Statistics"; - // + // // label12 - // + // this.label12.AutoSize = true; - this.label12.Location = new System.Drawing.Point(16, 100); - this.label12.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label12.Location = new System.Drawing.Point(12, 81); this.label12.Name = "label12"; - this.label12.Size = new System.Drawing.Size(51, 17); + this.label12.Size = new System.Drawing.Size(37, 13); this.label12.TabIndex = 19; this.label12.Text = "Errors:"; - // + // // lblParseErrorCount - // + // this.lblParseErrorCount.AutoSize = true; - this.lblParseErrorCount.Location = new System.Drawing.Point(144, 100); - this.lblParseErrorCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParseErrorCount.Location = new System.Drawing.Point(108, 81); this.lblParseErrorCount.Name = "lblParseErrorCount"; - this.lblParseErrorCount.Size = new System.Drawing.Size(16, 17); + this.lblParseErrorCount.Size = new System.Drawing.Size(13, 13); this.lblParseErrorCount.TabIndex = 18; this.lblParseErrorCount.Text = "0"; - // + // // label1 - // + // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(16, 73); - this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Location = new System.Drawing.Point(12, 59); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(110, 17); + this.label1.Size = new System.Drawing.Size(82, 13); this.label1.TabIndex = 17; this.label1.Text = "Parse Time, ms:"; - // + // // lblParseTime - // + // this.lblParseTime.AutoSize = true; - this.lblParseTime.Location = new System.Drawing.Point(144, 73); - this.lblParseTime.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblParseTime.Location = new System.Drawing.Point(108, 59); this.lblParseTime.Name = "lblParseTime"; - this.lblParseTime.Size = new System.Drawing.Size(16, 17); + this.lblParseTime.Size = new System.Drawing.Size(13, 13); this.lblParseTime.TabIndex = 16; this.lblParseTime.Text = "0"; - // + // // label7 - // + // this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(16, 20); - this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label7.Location = new System.Drawing.Point(12, 16); this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(46, 17); + this.label7.Size = new System.Drawing.Size(35, 13); this.label7.TabIndex = 15; this.label7.Text = "Lines:"; - // + // // lblSrcLineCount - // + // this.lblSrcLineCount.AutoSize = true; - this.lblSrcLineCount.Location = new System.Drawing.Point(144, 20); - this.lblSrcLineCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblSrcLineCount.Location = new System.Drawing.Point(108, 16); this.lblSrcLineCount.Name = "lblSrcLineCount"; - this.lblSrcLineCount.Size = new System.Drawing.Size(16, 17); + this.lblSrcLineCount.Size = new System.Drawing.Size(13, 13); this.lblSrcLineCount.TabIndex = 14; this.lblSrcLineCount.Text = "0"; - // + // // label3 - // + // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(16, 46); - this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label3.Location = new System.Drawing.Point(12, 37); this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(59, 17); + this.label3.Size = new System.Drawing.Size(46, 13); this.label3.TabIndex = 13; this.label3.Text = "Tokens:"; - // + // // lblSrcTokenCount - // + // this.lblSrcTokenCount.AutoSize = true; - this.lblSrcTokenCount.Location = new System.Drawing.Point(144, 46); - this.lblSrcTokenCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblSrcTokenCount.Location = new System.Drawing.Point(108, 37); this.lblSrcTokenCount.Name = "lblSrcTokenCount"; - this.lblSrcTokenCount.Size = new System.Drawing.Size(16, 17); + this.lblSrcTokenCount.Size = new System.Drawing.Size(13, 13); this.lblSrcTokenCount.TabIndex = 12; this.lblSrcTokenCount.Text = "0"; - // + // // pageParserTrace - // + // this.pageParserTrace.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.pageParserTrace.Controls.Add(this.grpParserActions); this.pageParserTrace.Controls.Add(this.splitter1); this.pageParserTrace.Controls.Add(this.grpTokens); this.pageParserTrace.Controls.Add(this.pnlParserTraceTop); - this.pageParserTrace.Location = new System.Drawing.Point(4, 25); - this.pageParserTrace.Margin = new System.Windows.Forms.Padding(4); + this.pageParserTrace.Location = new System.Drawing.Point(4, 22); this.pageParserTrace.Name = "pageParserTrace"; - this.pageParserTrace.Padding = new System.Windows.Forms.Padding(4); - this.pageParserTrace.Size = new System.Drawing.Size(1464, 201); + this.pageParserTrace.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageParserTrace.Size = new System.Drawing.Size(1096, 161); this.pageParserTrace.TabIndex = 3; this.pageParserTrace.Text = "Parser Trace"; this.pageParserTrace.UseVisualStyleBackColor = true; - // + // // grpParserActions - // + // this.grpParserActions.Controls.Add(this.gridParserTrace); this.grpParserActions.Dock = System.Windows.Forms.DockStyle.Fill; - this.grpParserActions.Location = new System.Drawing.Point(4, 34); - this.grpParserActions.Margin = new System.Windows.Forms.Padding(4); + this.grpParserActions.Location = new System.Drawing.Point(3, 28); this.grpParserActions.Name = "grpParserActions"; - this.grpParserActions.Padding = new System.Windows.Forms.Padding(4); - this.grpParserActions.Size = new System.Drawing.Size(1101, 161); + this.grpParserActions.Size = new System.Drawing.Size(823, 128); this.grpParserActions.TabIndex = 4; this.grpParserActions.TabStop = false; - // + // // gridParserTrace - // + // this.gridParserTrace.AllowUserToAddRows = false; this.gridParserTrace.AllowUserToDeleteRows = false; this.gridParserTrace.AllowUserToResizeRows = false; @@ -1049,23 +990,22 @@ private void InitializeComponent() { this.Input, this.Action}); this.gridParserTrace.Dock = System.Windows.Forms.DockStyle.Fill; - this.gridParserTrace.Location = new System.Drawing.Point(4, 19); - this.gridParserTrace.Margin = new System.Windows.Forms.Padding(4); + this.gridParserTrace.Location = new System.Drawing.Point(3, 16); this.gridParserTrace.MultiSelect = false; this.gridParserTrace.Name = "gridParserTrace"; this.gridParserTrace.ReadOnly = true; this.gridParserTrace.RowHeadersVisible = false; this.gridParserTrace.RowTemplate.Height = 24; this.gridParserTrace.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.CellSelect; - this.gridParserTrace.Size = new System.Drawing.Size(1093, 138); + this.gridParserTrace.Size = new System.Drawing.Size(817, 109); this.gridParserTrace.TabIndex = 0; this.gridParserTrace.CellDoubleClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridParserTrace_CellDoubleClick); - // + // // State - // + // this.State.DataPropertyName = "State"; - dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - this.State.DefaultCellStyle = dataGridViewCellStyle7; + dataGridViewCellStyle23.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + this.State.DefaultCellStyle = dataGridViewCellStyle23; this.State.HeaderText = "State"; this.State.Name = "State"; this.State.ReadOnly = true; @@ -1073,12 +1013,12 @@ private void InitializeComponent() { this.State.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.State.ToolTipText = "Double-click grid cell to navigate to state details"; this.State.Width = 60; - // + // // Stack - // + // this.Stack.DataPropertyName = "StackTop"; - dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleRight; - this.Stack.DefaultCellStyle = dataGridViewCellStyle8; + dataGridViewCellStyle24.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleRight; + this.Stack.DefaultCellStyle = dataGridViewCellStyle24; this.Stack.HeaderText = "Stack Top"; this.Stack.Name = "Stack"; this.Stack.ReadOnly = true; @@ -1086,9 +1026,9 @@ private void InitializeComponent() { this.Stack.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.Stack.ToolTipText = "Double-click grid cell to locate node in source code"; this.Stack.Width = 200; - // + // // Input - // + // this.Input.DataPropertyName = "Input"; this.Input.HeaderText = "Input"; this.Input.Name = "Input"; @@ -1096,121 +1036,112 @@ private void InitializeComponent() { this.Input.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.Input.ToolTipText = "Double-click grid cell to locate in source code"; this.Input.Width = 200; - // + // // Action - // + // this.Action.DataPropertyName = "Action"; this.Action.HeaderText = "Action"; this.Action.Name = "Action"; this.Action.ReadOnly = true; this.Action.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; this.Action.Width = 600; - // + // // splitter1 - // + // this.splitter1.BackColor = System.Drawing.SystemColors.Control; this.splitter1.Dock = System.Windows.Forms.DockStyle.Right; - this.splitter1.Location = new System.Drawing.Point(1105, 34); - this.splitter1.Margin = new System.Windows.Forms.Padding(4); + this.splitter1.Location = new System.Drawing.Point(826, 28); this.splitter1.Name = "splitter1"; - this.splitter1.Size = new System.Drawing.Size(8, 161); + this.splitter1.Size = new System.Drawing.Size(6, 128); this.splitter1.TabIndex = 15; this.splitter1.TabStop = false; - // + // // grpTokens - // + // this.grpTokens.Controls.Add(this.lstTokens); this.grpTokens.Dock = System.Windows.Forms.DockStyle.Right; - this.grpTokens.Location = new System.Drawing.Point(1113, 34); - this.grpTokens.Margin = new System.Windows.Forms.Padding(4); + this.grpTokens.Location = new System.Drawing.Point(832, 28); this.grpTokens.Name = "grpTokens"; - this.grpTokens.Padding = new System.Windows.Forms.Padding(4); - this.grpTokens.Size = new System.Drawing.Size(345, 161); + this.grpTokens.Size = new System.Drawing.Size(259, 128); this.grpTokens.TabIndex = 3; this.grpTokens.TabStop = false; this.grpTokens.Text = "Tokens"; - // + // // lstTokens - // + // this.lstTokens.Dock = System.Windows.Forms.DockStyle.Fill; this.lstTokens.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lstTokens.FormattingEnabled = true; - this.lstTokens.ItemHeight = 17; - this.lstTokens.Location = new System.Drawing.Point(4, 19); - this.lstTokens.Margin = new System.Windows.Forms.Padding(4); + this.lstTokens.ItemHeight = 14; + this.lstTokens.Location = new System.Drawing.Point(3, 16); this.lstTokens.Name = "lstTokens"; - this.lstTokens.Size = new System.Drawing.Size(337, 138); + this.lstTokens.Size = new System.Drawing.Size(253, 109); this.lstTokens.TabIndex = 2; this.lstTokens.Click += new System.EventHandler(this.lstTokens_Click); - // + // // pnlParserTraceTop - // + // this.pnlParserTraceTop.BackColor = System.Drawing.SystemColors.Control; this.pnlParserTraceTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.pnlParserTraceTop.Controls.Add(this.chkExcludeComments); this.pnlParserTraceTop.Controls.Add(this.lblTraceComment); this.pnlParserTraceTop.Controls.Add(this.chkParserTrace); this.pnlParserTraceTop.Dock = System.Windows.Forms.DockStyle.Top; - this.pnlParserTraceTop.Location = new System.Drawing.Point(4, 4); - this.pnlParserTraceTop.Margin = new System.Windows.Forms.Padding(4); + this.pnlParserTraceTop.Location = new System.Drawing.Point(3, 3); this.pnlParserTraceTop.Name = "pnlParserTraceTop"; - this.pnlParserTraceTop.Size = new System.Drawing.Size(1454, 30); + this.pnlParserTraceTop.Size = new System.Drawing.Size(1088, 25); this.pnlParserTraceTop.TabIndex = 1; - // + // // chkExcludeComments - // + // this.chkExcludeComments.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.chkExcludeComments.AutoSize = true; this.chkExcludeComments.Checked = true; this.chkExcludeComments.CheckState = System.Windows.Forms.CheckState.Checked; - this.chkExcludeComments.Location = new System.Drawing.Point(1250, 4); - this.chkExcludeComments.Margin = new System.Windows.Forms.Padding(4); + this.chkExcludeComments.Location = new System.Drawing.Point(929, 3); this.chkExcludeComments.Name = "chkExcludeComments"; - this.chkExcludeComments.Size = new System.Drawing.Size(186, 21); + this.chkExcludeComments.Size = new System.Drawing.Size(145, 17); this.chkExcludeComments.TabIndex = 2; this.chkExcludeComments.Text = "Exclude comment tokens"; this.chkExcludeComments.UseVisualStyleBackColor = true; - // + // // lblTraceComment - // + // this.lblTraceComment.AutoSize = true; this.lblTraceComment.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.lblTraceComment.ForeColor = System.Drawing.SystemColors.ControlDarkDark; - this.lblTraceComment.Location = new System.Drawing.Point(171, 4); - this.lblTraceComment.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblTraceComment.Location = new System.Drawing.Point(128, 3); this.lblTraceComment.Name = "lblTraceComment"; - this.lblTraceComment.Size = new System.Drawing.Size(470, 17); + this.lblTraceComment.Size = new System.Drawing.Size(350, 13); this.lblTraceComment.TabIndex = 1; this.lblTraceComment.Text = "(Double-click grid cell to navigate to parser state or source code position)"; - // + // // pageOutput - // + // this.pageOutput.Controls.Add(this.txtOutput); this.pageOutput.Controls.Add(this.pnlRuntimeInfo); - this.pageOutput.Location = new System.Drawing.Point(4, 25); - this.pageOutput.Margin = new System.Windows.Forms.Padding(4); + this.pageOutput.Location = new System.Drawing.Point(4, 22); this.pageOutput.Name = "pageOutput"; - this.pageOutput.Padding = new System.Windows.Forms.Padding(4); - this.pageOutput.Size = new System.Drawing.Size(1464, 201); + this.pageOutput.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3); + this.pageOutput.Size = new System.Drawing.Size(1096, 161); this.pageOutput.TabIndex = 0; this.pageOutput.Text = "Runtime Output"; this.pageOutput.UseVisualStyleBackColor = true; - // + // // txtOutput - // + // this.txtOutput.Dock = System.Windows.Forms.DockStyle.Fill; this.txtOutput.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.txtOutput.Location = new System.Drawing.Point(4, 4); - this.txtOutput.Margin = new System.Windows.Forms.Padding(4); + this.txtOutput.Location = new System.Drawing.Point(3, 3); this.txtOutput.Multiline = true; this.txtOutput.Name = "txtOutput"; this.txtOutput.ReadOnly = true; this.txtOutput.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.txtOutput.Size = new System.Drawing.Size(1255, 193); + this.txtOutput.Size = new System.Drawing.Size(939, 155); this.txtOutput.TabIndex = 1; - // + // // pnlRuntimeInfo - // + // this.pnlRuntimeInfo.Controls.Add(this.label14); this.pnlRuntimeInfo.Controls.Add(this.lblGCCount); this.pnlRuntimeInfo.Controls.Add(this.label13); @@ -1219,98 +1150,103 @@ private void InitializeComponent() { this.pnlRuntimeInfo.Controls.Add(this.label5); this.pnlRuntimeInfo.Controls.Add(this.lblRunTime); this.pnlRuntimeInfo.Dock = System.Windows.Forms.DockStyle.Right; - this.pnlRuntimeInfo.Location = new System.Drawing.Point(1259, 4); - this.pnlRuntimeInfo.Margin = new System.Windows.Forms.Padding(4); + this.pnlRuntimeInfo.Location = new System.Drawing.Point(942, 3); this.pnlRuntimeInfo.Name = "pnlRuntimeInfo"; - this.pnlRuntimeInfo.Size = new System.Drawing.Size(201, 193); + this.pnlRuntimeInfo.Size = new System.Drawing.Size(151, 155); this.pnlRuntimeInfo.TabIndex = 2; - // + // // label14 - // + // this.label14.AutoSize = true; - this.label14.Location = new System.Drawing.Point(8, 28); - this.label14.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label14.Location = new System.Drawing.Point(6, 23); this.label14.Name = "label14"; - this.label14.Size = new System.Drawing.Size(138, 17); + this.label14.Size = new System.Drawing.Size(105, 13); this.label14.TabIndex = 24; this.label14.Text = "GC Collection Count:"; - // + // // lblGCCount - // + // this.lblGCCount.AutoSize = true; - this.lblGCCount.Location = new System.Drawing.Point(165, 28); - this.lblGCCount.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblGCCount.Location = new System.Drawing.Point(124, 23); this.lblGCCount.Name = "lblGCCount"; - this.lblGCCount.Size = new System.Drawing.Size(16, 17); + this.lblGCCount.Size = new System.Drawing.Size(13, 13); this.lblGCCount.TabIndex = 23; this.lblGCCount.Text = "0"; - // + // // label13 - // + // this.label13.AutoSize = true; - this.label13.Location = new System.Drawing.Point(7, 51); - this.label13.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label13.Location = new System.Drawing.Point(5, 41); this.label13.Name = "label13"; - this.label13.Size = new System.Drawing.Size(99, 17); + this.label13.Size = new System.Drawing.Size(73, 13); this.label13.TabIndex = 22; this.label13.Text = "Runtime error:"; - // + // // lnkShowErrStack - // + // this.lnkShowErrStack.AutoSize = true; this.lnkShowErrStack.Enabled = false; - this.lnkShowErrStack.Location = new System.Drawing.Point(31, 106); - this.lnkShowErrStack.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lnkShowErrStack.Location = new System.Drawing.Point(23, 86); this.lnkShowErrStack.Name = "lnkShowErrStack"; - this.lnkShowErrStack.Size = new System.Drawing.Size(101, 17); + this.lnkShowErrStack.Size = new System.Drawing.Size(79, 13); this.lnkShowErrStack.TabIndex = 21; this.lnkShowErrStack.TabStop = true; this.lnkShowErrStack.Text = "Show full stack"; this.lnkShowErrStack.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkShowErrStack_LinkClicked); - // + // // lnkShowErrLocation - // + // this.lnkShowErrLocation.AutoSize = true; this.lnkShowErrLocation.Enabled = false; - this.lnkShowErrLocation.Location = new System.Drawing.Point(31, 76); - this.lnkShowErrLocation.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lnkShowErrLocation.Location = new System.Drawing.Point(23, 62); this.lnkShowErrLocation.Name = "lnkShowErrLocation"; - this.lnkShowErrLocation.Size = new System.Drawing.Size(130, 17); + this.lnkShowErrLocation.Size = new System.Drawing.Size(98, 13); this.lnkShowErrLocation.TabIndex = 20; this.lnkShowErrLocation.TabStop = true; this.lnkShowErrLocation.Text = "Show error location"; this.lnkShowErrLocation.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkShowErrLocation_LinkClicked); - // + // // label5 - // + // this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(7, 4); - this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label5.Location = new System.Drawing.Point(5, 3); this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(129, 17); + this.label5.Size = new System.Drawing.Size(98, 13); this.label5.TabIndex = 19; this.label5.Text = "Execution time, ms:"; - // + // // lblRunTime - // + // this.lblRunTime.AutoSize = true; - this.lblRunTime.Location = new System.Drawing.Point(164, 4); - this.lblRunTime.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.lblRunTime.Location = new System.Drawing.Point(123, 3); this.lblRunTime.Name = "lblRunTime"; - this.lblRunTime.Size = new System.Drawing.Size(16, 17); + this.lblRunTime.Size = new System.Drawing.Size(13, 13); this.lblRunTime.TabIndex = 18; this.lblRunTime.Text = "0"; - // + // + // btnRefresh + // + this.btnRefresh.Enabled = false; + this.btnRefresh.Location = new System.Drawing.Point(339, 2); + this.btnRefresh.Name = "btnRefresh"; + this.btnRefresh.Size = new System.Drawing.Size(56, 24); + this.btnRefresh.TabIndex = 14; + this.btnRefresh.Text = "Refresh"; + this.toolTip.SetToolTip(this.btnRefresh, "Reload grammar assembly and refresh the current grammar.\r\n" + + "Use Auto-refresh checkbox to do this automatically\r\nevery time the target assembly " + + "file is updated (recompiled)."); + this.btnRefresh.UseVisualStyleBackColor = true; + this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click); + // // fmGrammarExplorer - // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1472, 844); + this.ClientSize = new System.Drawing.Size(1022, 603); this.Controls.Add(this.tabGrammar); this.Controls.Add(this.splitBottom); this.Controls.Add(this.pnlLang); this.Controls.Add(this.tabBottom); - this.Margin = new System.Windows.Forms.Padding(4); this.Name = "fmGrammarExplorer"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Irony Grammar Explorer"; @@ -1455,6 +1391,7 @@ private void InitializeComponent() { private System.Windows.Forms.DataGridViewTextBoxColumn Input; private System.Windows.Forms.DataGridViewTextBoxColumn Action; private System.Windows.Forms.Button btnLocate; + private System.Windows.Forms.Button btnRefresh; } } diff --git a/Irony.GrammarExplorer/fmGrammarExplorer.cs b/Irony.GrammarExplorer/fmGrammarExplorer.cs index eed4f98..9cb44f7 100644 --- a/Irony.GrammarExplorer/fmGrammarExplorer.cs +++ b/Irony.GrammarExplorer/fmGrammarExplorer.cs @@ -3,8 +3,8 @@ * Copyright (c) Roman Ivantsov * This source code is subject to terms and conditions of the MIT License * for Irony. A copy of the license can be found in the License.txt file - * at the root of this distribution. - * By using this source code in any fashion, you are agreeing to be bound by the terms of the + * at the root of this distribution. + * By using this source code in any fashion, you are agreeing to be bound by the terms of the * MIT License. * You must not remove this notice from this software. * **********************************************************************************/ @@ -38,7 +38,7 @@ public fmGrammarExplorer() { //fields Grammar _grammar; - LanguageData _language; + LanguageData _language; Parser _parser; ParseTree _parseTree; ScriptException _runtimeError; @@ -70,10 +70,10 @@ private void fmExploreGrammar_FormClosing(object sender, FormClosingEventArgs e) Settings.Default.DisableHili = chkDisableHili.Checked; Settings.Default.AutoRefresh = chkAutoRefresh.Checked; var grammars = GrammarItemList.FromCombo(cboGrammars); - Settings.Default.Grammars = grammars.ToXml(); + Settings.Default.Grammars = grammars.ToXml(); Settings.Default.Save(); }//method - #endregion + #endregion #region Show... methods //Show... methods ###################################################################################################################### @@ -95,7 +95,7 @@ private void ClearParserOutput() { gridParserTrace.Rows.Clear(); lstTokens.Items.Clear(); tvParseTree.Nodes.Clear(); - tvAst.Nodes.Clear(); + tvAst.Nodes.Clear(); Application.DoEvents(); } @@ -111,10 +111,10 @@ private void ShowLanguageInfo() { private void ShowCompilerErrors() { gridCompileErrors.Rows.Clear(); - if (_parseTree == null || _parseTree.ParserMessages.Count == 0) return; - foreach (var err in _parseTree.ParserMessages) + if (_parseTree == null || _parseTree.ParserMessages.Count == 0) return; + foreach (var err in _parseTree.ParserMessages) gridCompileErrors.Rows.Add(err.Location, err, err.ParserState); - var needPageSwitch = tabBottom.SelectedTab != pageParserOutput && + var needPageSwitch = tabBottom.SelectedTab != pageParserOutput && !(tabBottom.SelectedTab == pageParserTrace && chkParserTrace.Checked); if (needPageSwitch) tabBottom.SelectedTab = pageParserOutput; @@ -123,13 +123,13 @@ private void ShowCompilerErrors() { private void ShowParseTrace() { gridParserTrace.Rows.Clear(); foreach (var entry in _parser.Context.ParserTrace) { - int index = gridParserTrace.Rows.Add(entry.State, entry.StackTop, entry.Input, entry.Message); + int index = gridParserTrace.Rows.Add(entry.State, entry.StackTop, entry.Input, entry.Message); if (entry.IsError) gridParserTrace.Rows[gridParserTrace.Rows.Count - 1].DefaultCellStyle.ForeColor = Color.Red; } //Show tokens foreach (Token tkn in _parseTree.Tokens) { - if (chkExcludeComments.Checked && tkn.Category == TokenCategory.Comment) continue; + if (chkExcludeComments.Checked && tkn.Category == TokenCategory.Comment) continue; lstTokens.Items.Add(tkn); } }//method @@ -143,19 +143,19 @@ private void ShowCompileStats() { lblParseTime.Text = _parseTree.ParseTimeMilliseconds.ToString(); lblParseErrorCount.Text = _parseTree.ParserMessages.Count.ToString(); Application.DoEvents(); - //Note: this time is "pure" parse time; actual delay after cliking "Compile" includes time to fill ParseTree, AstTree controls + //Note: this time is "pure" parse time; actual delay after cliking "Compile" includes time to fill ParseTree, AstTree controls } private void ShowParseTree() { tvParseTree.Nodes.Clear(); - if (_parseTree == null) return; + if (_parseTree == null) return; AddParseNodeRec(null, _parseTree.Root); } private void AddParseNodeRec(TreeNode parent, ParseTreeNode node) { if (node == null) return; string txt = node.ToString(); TreeNode tvNode = (parent == null? tvParseTree.Nodes.Add(txt) : parent.Nodes.Add(txt) ); - tvNode.Tag = node; + tvNode.Tag = node; foreach(var child in node.ChildNodes) AddParseNodeRec(tvNode, child); } @@ -167,20 +167,20 @@ private void ShowAstTree() { } private void AddAstNodeRec(TreeNode parent, object astNode) { - if (astNode == null) return; + if (astNode == null) return; string txt = astNode.ToString(); TreeNode newNode = (parent == null ? tvAst.Nodes.Add(txt) : parent.Nodes.Add(txt)); newNode.Tag = astNode; var iBrowsable = astNode as IBrowsableAstNode; if (iBrowsable == null) return; - var childList = iBrowsable.GetChildNodes(); + var childList = iBrowsable.GetChildNodes(); foreach (var child in childList) AddAstNodeRec(newNode, child); } private void ShowParserConstructionResults() { - lblParserStateCount.Text = _language.ParserData.States.Count.ToString(); + lblParserStateCount.Text = _language.ParserData.States.Count.ToString(); lblParserConstrTime.Text = _language.ConstructionTime.ToString(); txtParserStates.Text = string.Empty; gridGrammarErrors.Rows.Clear(); @@ -188,7 +188,7 @@ private void ShowParserConstructionResults() { txtNonTerms.Text = string.Empty; txtParserStates.Text = string.Empty; tabBottom.SelectedTab = pageLanguage; - if (_parser == null) return; + if (_parser == null) return; txtTerms.Text = ParserDataPrinter.PrintTerminals(_language); txtNonTerms.Text = ParserDataPrinter.PrintNonTerminals(_language); txtParserStates.Text = ParserDataPrinter.PrintStateList(_language); @@ -240,18 +240,18 @@ private void LocateParserState(ParserState state) { private void ShowRuntimeError(ScriptException error){ _runtimeError = error; lnkShowErrLocation.Enabled = _runtimeError != null; - lnkShowErrStack.Enabled = lnkShowErrLocation.Enabled; + lnkShowErrStack.Enabled = lnkShowErrLocation.Enabled; if (_runtimeError != null) { //the exception was caught and processed by Interpreter WriteOutput("Error: " + error.Message + " At " + _runtimeError.Location.ToUiString() + "."); - ShowSourcePosition(_runtimeError.Location.Position, 1); + ShowSourcePosition(_runtimeError.Location.Position, 1); } else { //the exception was not caught by interpreter/AST node. Show full exception info WriteOutput("Error: " + error.Message); fmShowException.ShowException(error); } - tabBottom.SelectedTab = pageOutput; + tabBottom.SelectedTab = pageOutput; } private void SelectTreeNode(TreeView tree, TreeNode node) { @@ -266,12 +266,12 @@ private void SelectTreeNode(TreeView tree, TreeNode node) { private void ClearRuntimeInfo() { lnkShowErrLocation.Enabled = false; - lnkShowErrStack.Enabled = false; + lnkShowErrStack.Enabled = false; _runtimeError = null; txtOutput.Text = string.Empty; } - #endregion + #endregion #region Grammar combo menu commands private void menuGrammars_Opening(object sender, CancelEventArgs e) { @@ -281,14 +281,15 @@ private void menuGrammars_Opening(object sender, CancelEventArgs e) { private void miAdd_Click(object sender, EventArgs e) { if (dlgSelectAssembly.ShowDialog() != DialogResult.OK) return; string location = dlgSelectAssembly.FileName; - if (string.IsNullOrEmpty(location)) return; - var oldGrammars = new GrammarItemList(); + if (string.IsNullOrEmpty(location)) return; + var oldGrammars = new GrammarItemList(); foreach(var item in cboGrammars.Items) oldGrammars.Add((GrammarItem) item); var grammars = fmSelectGrammars.SelectGrammars(location, oldGrammars); if (grammars == null) return; foreach (GrammarItem item in grammars) cboGrammars.Items.Add(item); + btnRefresh.Enabled = false; // auto-select the first grammar if no grammar currently selected if (cboGrammars.SelectedIndex < 0 && grammars.Count > 0) cboGrammars.SelectedIndex = 0; @@ -301,6 +302,8 @@ private void miRemove_Click(object sender, EventArgs e) { _parser = null; if (cboGrammars.Items.Count > 0) cboGrammars.SelectedIndex = 0; + else + btnRefresh.Enabled = false; } } @@ -308,6 +311,7 @@ private void miRemoveAll_Click(object sender, EventArgs e) { if (MessageBox.Show("Are you sure you want to remove all grammmars in the list?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { cboGrammars.Items.Clear(); + btnRefresh.Enabled = false; _parser = null; } } @@ -324,8 +328,8 @@ private void CreateParser() { txtOutput.Text = string.Empty; _parseTree = null; - btnRun.Enabled = _grammar is ICanRunSample; - _language = new LanguageData(_grammar); + btnRun.Enabled = _grammar is ICanRunSample; + _language = new LanguageData(_grammar); _parser = new Parser (_language); ShowParserConstructionResults(); StartHighlighter(); @@ -333,7 +337,7 @@ private void CreateParser() { private void ParseSample() { ClearParserOutput(); - if (_parser == null || !_parser.Language.CanParse()) return; + if (_parser == null || !_parser.Language.CanParse()) return; _parseTree = null; GC.Collect(); //to avoid disruption of perf times with occasional collections _parser.Context.TracingEnabled = chkParserTrace.Checked; @@ -351,7 +355,7 @@ private void ParseSample() { } ShowCompileStats(); ShowParseTree(); - ShowAstTree(); + ShowAstTree(); } } @@ -366,28 +370,28 @@ private void RunSample() { if (_parseTree.ParserMessages.Count > 0) return; GC.Collect(); //to avoid disruption of perf times with occasional collections - oldGcCount = GC.CollectionCount(0); + oldGcCount = GC.CollectionCount(0); System.Threading.Thread.Sleep(100); - sw.Start(); + sw.Start(); var iRunner = _grammar as ICanRunSample; - var args = new RunSampleArgs(_language, txtSource.Text, _parseTree); - string output = iRunner.RunSample(args); + var args = new RunSampleArgs(_language, txtSource.Text, _parseTree); + string output = iRunner.RunSample(args); sw.Stop(); lblRunTime.Text = sw.ElapsedMilliseconds.ToString(); var gcCount = GC.CollectionCount(0) - oldGcCount; - lblGCCount.Text = gcCount.ToString(); + lblGCCount.Text = gcCount.ToString(); WriteOutput(output); tabBottom.SelectedTab = pageOutput; } catch (ScriptException ex) { - ShowRuntimeError(ex); + ShowRuntimeError(ex); } finally { sw.Stop(); }//finally }//method private void WriteOutput(string text) { - if (string.IsNullOrEmpty(text)) return; + if (string.IsNullOrEmpty(text)) return; txtOutput.Text += text + Environment.NewLine; txtOutput.Select(txtOutput.Text.Length - 1, 0); } @@ -403,7 +407,7 @@ private void LoadSourceFile(string path) { txtSource.Text = null; //to clear any old formatting txtSource.Text = reader.ReadToEnd(); txtSource.Select(0, 0); - + } catch (Exception e) { MessageBox.Show(e.Message); } finally { @@ -412,13 +416,13 @@ private void LoadSourceFile(string path) { } } - //Source highlighting + //Source highlighting RichTextBoxHighlighter _highlighter; private void StartHighlighter() { if (_highlighter != null) StopHighlighter(); - if (chkDisableHili.Checked) return; - if (!_parser.Language.CanParse()) return; + if (chkDisableHili.Checked) return; + if (!_parser.Language.CanParse()) return; _highlighter = new RichTextBoxHighlighter(txtSource, _language); _highlighter.Adapter.Activate(); } @@ -426,18 +430,18 @@ private void StopHighlighter() { if (_highlighter == null) return; _highlighter.Dispose(); _highlighter = null; - ClearHighlighting(); + ClearHighlighting(); } private void ClearHighlighting() { var txt = txtSource.Text; - txtSource.Clear(); + txtSource.Clear(); txtSource.Text = txt; //remove all old highlighting } private void EnableHighlighter(bool enable) { if (_highlighter != null) StopHighlighter(); if (enable) - StartHighlighter(); + StartHighlighter(); } //The following methods are contributed by Andrew Bradnan; pasted here with minor changes @@ -500,8 +504,8 @@ private void btnRun_Click(object sender, EventArgs e) { } private void tvParseTree_AfterSelect(object sender, TreeViewEventArgs e) { - if (_treeClickDisabled) - return; + if (_treeClickDisabled) + return; var vtreeNode = tvParseTree.SelectedNode; if (vtreeNode == null) return; var parseNode = vtreeNode.Tag as ParseTreeNode; @@ -524,7 +528,7 @@ private void LoadSelectedGrammar() { try { ClearLanguageInfo(); ClearParserOutput(); - ClearRuntimeInfo(); + ClearRuntimeInfo(); _changingGrammar = true; CreateGrammar(); @@ -533,6 +537,7 @@ private void LoadSelectedGrammar() { } finally { _changingGrammar = false; //in case of exception } + btnRefresh.Enabled = true; } private void cboGrammars_SelectedIndexChanged(object sender, EventArgs e) { @@ -551,6 +556,10 @@ private void GrammarAssemblyUpdated(object sender, EventArgs args) { } } + private void btnRefresh_Click(object sender, EventArgs e) { + LoadSelectedGrammar(); + } + private void btnFileOpen_Click(object sender, EventArgs e) { if (dlgOpenFile.ShowDialog() != DialogResult.OK) return; ClearParserOutput(); @@ -568,7 +577,7 @@ private void btnManageGrammars_Click(object sender, EventArgs e) { private void cboParseMethod_SelectedIndexChanged(object sender, EventArgs e) { //changing grammar causes setting of parse method combo, so to prevent double-call to ConstructParser // we don't do it here if _changingGrammar is set - if (!_changingGrammar) + if (!_changingGrammar) CreateParser(); } @@ -631,7 +640,7 @@ private void txtSearch_KeyPress(object sender, KeyPressEventArgs e) { private void lnkShowErrLocation_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { if (_runtimeError != null) - ShowSourcePosition(_runtimeError.Location.Position, 1); + ShowSourcePosition(_runtimeError.Location.Position, 1); } private void lnkShowErrStack_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { @@ -639,7 +648,7 @@ private void lnkShowErrStack_LinkClicked(object sender, LinkLabelLinkClickedEven if (_runtimeError.InnerException != null) fmShowException.ShowException(_runtimeError.InnerException); else - fmShowException.ShowException(_runtimeError); + fmShowException.ShowException(_runtimeError); } private void btnLocate_Click(object sender, EventArgs e) { @@ -647,7 +656,7 @@ private void btnLocate_Click(object sender, EventArgs e) { ParseSample(); var p = txtSource.SelectionStart; tvParseTree.SelectedNode = null; //just in case we won't find - tvAst.SelectedNode = null; + tvAst.SelectedNode = null; SelectTreeNode(tvParseTree, LocateTreeNode(tvParseTree.Nodes, p, node => (node.Tag as ParseTreeNode).Span.Location.Position)); SelectTreeNode(tvAst, LocateTreeNode(tvAst.Nodes, p, node => (node.Tag as IBrowsableAstNode).Position)); txtSource.Focus(); //set focus back to source @@ -655,14 +664,14 @@ private void btnLocate_Click(object sender, EventArgs e) { private TreeNode LocateTreeNode(TreeNodeCollection nodes, int position, Func positionFunction) { TreeNode current = null; - //Find the last node in the list that is "before or at" the position + //Find the last node in the list that is "before or at" the position foreach (TreeNode node in nodes) { if (positionFunction(node) > position) break; //from loop current = node; } //if current has children, search them - if (current != null && current.Nodes.Count > 0) - current = LocateTreeNode(current.Nodes, position, positionFunction) ?? current; + if (current != null && current.Nodes.Count > 0) + current = LocateTreeNode(current.Nodes, position, positionFunction) ?? current; return current; } diff --git a/Irony.GrammarExplorer/fmGrammarExplorer.resx b/Irony.GrammarExplorer/fmGrammarExplorer.resx index 8b472b6..97c38da 100644 --- a/Irony.GrammarExplorer/fmGrammarExplorer.resx +++ b/Irony.GrammarExplorer/fmGrammarExplorer.resx @@ -120,6 +120,9 @@ 454, 17 + + 454, 17 + If you check this box, the Grammar Explorer will automatically reload the grammar definition from the containing assembly whenever the target assembly is updated (recompiled). @@ -145,6 +148,15 @@ Simply restart the Grammar Explorer to clean-up the memory. True + + True + + + True + + + True + True @@ -154,6 +166,27 @@ Simply restart the Grammar Explorer to clean-up the memory. True + + True + + + True + + + True + + + True + + + True + + + True + + + True + True From e6ed5d02dcf3389fbfcb34b8e8eeff9715e44529 Mon Sep 17 00:00:00 2001 From: Alexey Yakovlev Date: Sat, 10 Mar 2012 22:32:02 +0400 Subject: [PATCH 5/5] Simplified Assembly.LoadFrom(fileName) results check: now we have a HashSet of loaded assemblies instead of a Dictionary. --- Irony.GrammarExplorer/GrammarLoader.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Irony.GrammarExplorer/GrammarLoader.cs b/Irony.GrammarExplorer/GrammarLoader.cs index 6723bbe..e4aa80f 100644 --- a/Irony.GrammarExplorer/GrammarLoader.cs +++ b/Irony.GrammarExplorer/GrammarLoader.cs @@ -30,7 +30,7 @@ class GrammarLoader { private static HashSet _probingPaths = new HashSet(); private Dictionary _cachedGrammarAssemblies = new Dictionary(); private static Dictionary _loadedAssembliesByNames = new Dictionary(); - private static Dictionary _loadedAssembliesByLocation = new Dictionary(); + private static HashSet _loadedAssemblies = new HashSet(); private static bool _enableBrowsingForAssemblyResolution = false; static GrammarLoader() { @@ -190,13 +190,11 @@ public static Assembly LoadAssembly(string fileName) { // try to load assembly using the standard policy var assembly = Assembly.LoadFrom(fileName); // if the standard policy returned the old version, force reload - Assembly oldVersion; - if (_loadedAssembliesByLocation.TryGetValue(fileName, out oldVersion)) { - if (assembly == oldVersion) + if (_loadedAssemblies.Contains(assembly)) { assembly = Assembly.Load(File.ReadAllBytes(fileName)); } - else // cache the loaded assembly by its location - _loadedAssembliesByLocation[fileName] = assembly; + // cache the loaded assembly by its location + _loadedAssemblies.Add(assembly); return assembly; } }