- using System;
- using System.Collections.Generic;
- using Ink;
- namespace Ink
- {
- public class Compiler
- {
- public class Options
- {
- public string sourceFilename;
- public List<string> pluginDirectories;
- public bool countAllVisits;
- public Ink.ErrorHandler errorHandler;
- public Ink.IFileHandler fileHandler;
- }
- public Parsed.Story parsedStory {
- get {
- return _parsedStory;
- }
- }
- public Compiler (string inkSource, Options options = null)
- {
- _inputString = inkSource;
- _options = options ?? new Options();
- if( _options.pluginDirectories != null )
- _pluginManager = new PluginManager (_options.pluginDirectories);
- }
- public Parsed.Story Parse()
- {
- _parser = new InkParser(_inputString, _options.sourceFilename, OnParseError, _options.fileHandler);
- _parsedStory = _parser.Parse();
- return _parsedStory;
- }
- public Runtime.Story Compile ()
- {
- if( _pluginManager != null )
- _inputString = _pluginManager.PreParse(_inputString);
- Parse();
- if( _pluginManager != null )
- _parsedStory = _pluginManager.PostParse(_parsedStory);
- if (_parsedStory != null && !_hadParseError) {
- _parsedStory.countAllVisits = _options.countAllVisits;
- _runtimeStory = _parsedStory.ExportRuntime (_options.errorHandler);
- if( _pluginManager != null )
- _runtimeStory = _pluginManager.PostExport (_parsedStory, _runtimeStory);
- } else {
- _runtimeStory = null;
- }
- return _runtimeStory;
- }
- public class CommandLineInputResult {
- public bool requestsExit;
- public int choiceIdx = -1;
- public string divertedPath;
- public string output;
- }
- public CommandLineInputResult HandleInput (CommandLineInput inputResult)
- {
- var result = new CommandLineInputResult ();
- // Request for debug source line number
- if (inputResult.debugSource != null) {
- var offset = (int)inputResult.debugSource;
- var dm = DebugMetadataForContentAtOffset (offset);
- if (dm != null)
- result.output = "DebugSource: " + dm.ToString ();
- else
- result.output = "DebugSource: Unknown source";
- }
- // Request for runtime path lookup (to line number)
- else if (inputResult.debugPathLookup != null) {
- var pathStr = inputResult.debugPathLookup;
- var contentResult = _runtimeStory.ContentAtPath (new Runtime.Path (pathStr));
- var dm = contentResult.obj.debugMetadata;
- if( dm != null )
- result.output = "DebugSource: " + dm.ToString ();
- else
- result.output = "DebugSource: Unknown source";
- }
- // User entered some ink
- else if (inputResult.userImmediateModeStatement != null) {
- var parsedObj = inputResult.userImmediateModeStatement as Parsed.Object;
- return ExecuteImmediateStatement(parsedObj);
- } else {
- return null;
- }
- return result;
- }
- CommandLineInputResult ExecuteImmediateStatement(Parsed.Object parsedObj) {
- var result = new CommandLineInputResult ();
- // Variable assignment: create in Parsed.Story as well as the Runtime.Story
- // so that we don't get an error message during reference resolution
- if (parsedObj is Parsed.VariableAssignment) {
- var varAssign = (Parsed.VariableAssignment)parsedObj;
- if (varAssign.isNewTemporaryDeclaration) {
- _parsedStory.TryAddNewVariableDeclaration (varAssign);
- }
- }
- parsedObj.parent = _parsedStory;
- var runtimeObj = parsedObj.runtimeObject;
- parsedObj.ResolveReferences (_parsedStory);
- if (!_parsedStory.hadError) {
- // Divert
- if (parsedObj is Parsed.Divert) {
- var parsedDivert = parsedObj as Parsed.Divert;
- result.divertedPath = parsedDivert.runtimeDivert.targetPath.ToString();
- }
- // Expression or variable assignment
- else if (parsedObj is Parsed.Expression || parsedObj is Parsed.VariableAssignment) {
- var evalResult = _runtimeStory.EvaluateExpression ((Runtime.Container)runtimeObj);
- if (evalResult != null) {
- result.output = evalResult.ToString ();
- }
- }
- } else {
- _parsedStory.ResetError ();
- }
- return result;
- }
- public void RetrieveDebugSourceForLatestContent ()
- {
- foreach (var outputObj in _runtimeStory.state.outputStream) {
- var textContent = outputObj as Runtime.StringValue;
- if (textContent != null) {
- var range = new DebugSourceRange ();
- range.length = textContent.value.Length;
- range.debugMetadata = textContent.debugMetadata;
- range.text = textContent.value;
- _debugSourceRanges.Add (range);
- }
- }
- }
- Runtime.DebugMetadata DebugMetadataForContentAtOffset (int offset)
- {
- int currOffset = 0;
- Runtime.DebugMetadata lastValidMetadata = null;
- foreach (var range in _debugSourceRanges) {
- if (range.debugMetadata != null)
- lastValidMetadata = range.debugMetadata;
- if (offset >= currOffset && offset < currOffset + range.length)
- return lastValidMetadata;
- currOffset += range.length;
- }
- return null;
- }
- public struct DebugSourceRange
- {
- public int length;
- public Runtime.DebugMetadata debugMetadata;
- public string text;
- }
- // Need to wrap the error handler so that we know
- // when there was a critical error between parse and codegen stages
- void OnParseError (string message, ErrorType errorType)
- {
- if( errorType == ErrorType.Error )
- _hadParseError = true;
-
- if (_options.errorHandler != null)
- _options.errorHandler (message, errorType);
- else
- throw new System.Exception(message);
- }
- string _inputString;
- Options _options;
- InkParser _parser;
- Parsed.Story _parsedStory;
- Runtime.Story _runtimeStory;
- PluginManager _pluginManager;
- bool _hadParseError;
- List<DebugSourceRange> _debugSourceRanges = new List<DebugSourceRange> ();
- }
- }