/// <summary> /// 构建抽象语法树 /// </summary> /// <param name="tokens">Token序列内容</param> /// <param name="identifier">源文件ID</param> /// <returns></returns> public static ScopeExpression Parse(IEnumerable <BasicToken> tokens, CodeIdentifier identifier) { return(new Parser(tokens, identifier).Parse()); }
private Parser(IEnumerable <BasicToken> tokens, CodeIdentifier identifier) { Tokens = new SourceTokens(tokens); Identifier = identifier; }
/// <summary> /// 创建一个编译错误 /// </summary> /// <param name="identifier">目标文件</param> /// <param name="position">错误位置</param> /// <param name="message">错误信息</param> public CompileException(CodeIdentifier identifier, SourcePosition position, string message) : base($"{message} (at {identifier.Id}[{identifier.Hash}]:{position.Line + 1}:{position.Column + 1})") { }
/// <summary> /// 编译文件 /// </summary> /// <param name="path">脚本ID</param> /// <param name="forceCompile">是否强制重新编译</param> /// <returns>发生变化的文件列表</returns> public static IEnumerable <string> CompileAsset(string path, bool forceCompile = false) { if (!Application.isEditor) { throw new NotSupportedException($"Cannot compile {path}: static file compiler can only run in editor mode"); } if (!path.EndsWith(".vns")) { throw new NotSupportedException($"Cannot compile {path}: file name extension must be .vns"); } var option = ScriptInformation.CreateInformationFromAsset(path); if (option == null) { throw new NullReferenceException($"Cannot compile {path}: target outside of source folder or target is not acceptable script/binary"); } var changedFiles = new List <string>(); var source = File.ReadAllText(path, Encoding.UTF8).UnifyLineBreak(); if (!option.Hash.HasValue) { option.Hash = Hasher.Crc32(Encoding.UTF8.GetBytes(source)); } var identifier = new CodeIdentifier { Id = option.Id, Hash = option.Hash.Value }; // 编译文件 if (!forceCompile) { if (option.RecordedHash.HasValue && option.RecordedHash.Value == identifier.Hash) { return(new string[] { }); // 如果源代码内容没有变化则直接跳过编译 } } var(content, defaultTranslation) = CompileCode(source, identifier); var binaryFile = option.BinaryAssetPath(); File.WriteAllBytes(binaryFile, content); option.RecordedHash = option.Hash = identifier.Hash; changedFiles.Add(binaryFile); // 处理其他翻译 foreach (var(language, _) in option.Translations) { var languageFile = option.LanguageAssetPath(language); if (File.Exists(languageFile)) { var existedTranslation = new ScriptTranslation(File.ReadAllText(languageFile)); if (!existedTranslation.MergeWith(defaultTranslation)) { continue; } existedTranslation.SaveToAsset(languageFile); changedFiles.Add(languageFile); } else { // 如果翻译不存在,以默认翻译为蓝本新建翻译文件 defaultTranslation.SaveToAsset(languageFile); changedFiles.Add(languageFile); } } CompileConfiguration.Save(); return(changedFiles); }
/// <summary> /// 编译源代码 /// </summary> /// <param name="source">源代码文本</param> /// <param name="identifier">源代码脚本ID</param> /// <returns></returns> public static (byte[] Content, ScriptTranslation DefaultTranslation) CompileCode(string source, CodeIdentifier identifier) { return(ByteCodeGenerator.Generate(Parser.Parse(Lexer.Lex(source, identifier), identifier), identifier)); }
/// <summary> /// 生成字节码 /// </summary> /// <param name="root">根表达式</param> /// <param name="identifier">源文件ID</param> /// <returns></returns> public static (byte[] Content, ScriptTranslation DefaultTranslations) Generate(ScopeExpression root, CodeIdentifier identifier) { var context = new ByteCodeGeneratorContext(); // 生成主程序段 Generate(context, root); // 生成各种段 var(code, labels, strings, positions, translations) = context.File.CreateSegments(); context.File.Close(); // 准备目标文件 var targetFile = new ByteCodeWriter(); // 写入文件标志 ("VisualNovelScript Version 1, assembly format"的CRC32) targetFile.DirectWrite(0x963EFE4A); // 写入源文件哈希用于跳过重复编译 targetFile.DirectWrite(identifier.Hash); // 写入各种段 targetFile.DirectWrite(translations.ToByteArray()); targetFile.DirectWrite(strings); targetFile.DirectWrite(labels); targetFile.DirectWrite(positions); targetFile.DirectWrite(code); return(targetFile.CreateMainSegment(), translations); }