private Token ReadKeywordToken() { var spanStart = offset; char c; if (!TryPeekChar(out c) || c < 'a' || c > 'z') { return(ReadReservedToken(spanStart)); } var builder = new StringBuilder(); while (TryReadIdentifierChar(out c)) { builder.Append(c); } var span = new SourceSpan(document, spanStart, offset - spanStart); var result = builder.ToString(); // Some floating point tokens look like keywords, so we'll handle // them here as well as in the FP parsing routine. if (result == "nan") { return(new Token(TokenKind.Float, span, FloatLiteral.NaN(false))); } else if (result.StartsWith("nan:0x", StringComparison.Ordinal)) { var payload = result.Substring("nan:0x".Length); long newBits = 0; for (int i = 0; i < payload.Length; i++) { int digit; if (payload[i] == '_') { continue; } else if (TryParseHexDigit(payload[i], out digit)) { newBits = newBits * 16 + digit; } else { return(new Token(TokenKind.Keyword, span, result)); } } return(new Token(TokenKind.Float, span, FloatLiteral.NaN(false, newBits))); } else if (result == "inf") { return(new Token(TokenKind.Float, span, FloatLiteral.PositiveInfinity)); } else { return(new Token(TokenKind.Keyword, span, result)); } }
private bool TryReadUnsignedNumber(bool negate, out object result) { if (Expect("nan:0x")) { BigInteger hexNum; if (TryReadHexNum(out hexNum)) { result = FloatLiteral.NaN(negate, (long)hexNum); return(true); } else { result = FloatLiteral.NaN(negate); return(false); } } else if (Expect("nan")) { result = FloatLiteral.NaN(negate); return(true); } else if (Expect("inf")) { result = MaybeNegate(FloatLiteral.PositiveInfinity, negate); return(true); } else if (Expect("0x")) { return(TryReadUnsignedNumber( negate, out result, TryReadHexNum, TryReadHexFrac, 'p', 2)); } else { return(TryReadUnsignedNumber( negate, out result, TryReadNum, TryReadFrac, 'e', 10)); } }
/// <summary> /// Runs a single expression in the script. /// </summary> /// <param name="expression">The expression to run.</param> public TestStatistics Run(SExpression expression) { if (expression.IsCallTo("module")) { var module = Assembler.AssembleModule(expression, out string moduleId); var instance = Wasm.Interpret.ModuleInstance.Instantiate( module, importer, policy: ExecutionPolicy.Create(maxMemorySize: 0x1000), compiler: Compiler); moduleInstances.Add(instance); if (moduleId != null) { moduleInstancesByName[moduleId] = instance; } if (module.StartFunctionIndex.HasValue) { instance.Functions[(int)module.StartFunctionIndex.Value].Invoke(Array.Empty <object>()); } return(TestStatistics.Empty); } else if (expression.IsCallTo("register")) { var tail = expression.Tail; var name = Assembler.AssembleString(tail[0], Log); tail = tail.Skip(1).ToArray(); var moduleId = Assembler.AssembleLabelOrNull(ref tail); if (moduleId == null) { importer.RegisterImporter(name, new ModuleExportsImporter(moduleInstances[moduleInstances.Count - 1])); } else { importer.RegisterImporter(name, new ModuleExportsImporter(moduleInstancesByName[moduleId])); } return(TestStatistics.Empty); } else if (expression.IsCallTo("invoke") || expression.IsCallTo("get")) { RunAction(expression); return(TestStatistics.SingleSuccess); } else if (expression.IsCallTo("assert_return")) { var results = RunAction(expression.Tail[0]); var expected = expression.Tail .Skip(1) .Zip(results, (expr, val) => EvaluateConstExpr(expr, val.GetType())) .ToArray(); if (expected.Length != results.Count) { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action produced result ", string.Join(", ", results), "; expected ", string.Join(", ", expected), ".", Assembler.Highlight(expression))); return(TestStatistics.SingleFailure); } bool failures = false; for (int i = 0; i < expected.Length; i++) { if (!object.Equals(results[i], expected[i])) { if (AlmostEquals(results[i], expected[i])) { Log.Log( new LogEntry( Severity.Warning, "rounding error", "action produced result ", results[i].ToString(), "; expected ", expected[i].ToString(), ".", Assembler.Highlight(expression))); } else { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action produced result ", results[i].ToString(), "; expected ", expected[i].ToString(), ".", Assembler.Highlight(expression))); failures = true; } } } return(failures ? TestStatistics.SingleFailure : TestStatistics.SingleSuccess); } else if (expression.IsCallTo("assert_trap") || expression.IsCallTo("assert_exhaustion")) { var expected = Assembler.AssembleString(expression.Tail[1], Log); bool caught = false; Exception exception = null; try { if (expression.Tail[0].IsCallTo("module")) { Run(expression.Tail[0]); } else { RunAction(expression.Tail[0], false); } } catch (TrapException ex) { caught = ex.SpecMessage == expected; exception = ex; } catch (PixieException) { throw; } catch (Exception ex) { caught = false; exception = ex; } if (caught) { return(TestStatistics.SingleSuccess); } else { if (exception == null) { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action should have trapped, but didn't.", Assembler.Highlight(expression))); } else { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action trapped as expected, but with an unexpected exception. ", exception.ToString(), Assembler.Highlight(expression))); } return(TestStatistics.SingleFailure); } } else if (expression.IsCallTo("assert_return_canonical_nan")) { var results = RunAction(expression.Tail[0]); bool isCanonicalNaN; if (results.Count != 1) { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action produced ", results.Count.ToString(), " results (", string.Join(", ", results), "); expected a single canonical NaN.", Assembler.Highlight(expression))); return(TestStatistics.SingleFailure); } else if (results[0] is double) { var val = Interpret.ValueHelpers.ReinterpretAsInt64((double)results[0]); isCanonicalNaN = val == Interpret.ValueHelpers.ReinterpretAsInt64((double)FloatLiteral.NaN(false)) || val == Interpret.ValueHelpers.ReinterpretAsInt64((double)FloatLiteral.NaN(true)); } else if (results[0] is float) { var val = Interpret.ValueHelpers.ReinterpretAsInt32((float)results[0]); isCanonicalNaN = val == Interpret.ValueHelpers.ReinterpretAsInt32((float)FloatLiteral.NaN(false)) || val == Interpret.ValueHelpers.ReinterpretAsInt32((float)FloatLiteral.NaN(true)); } else { isCanonicalNaN = false; } if (isCanonicalNaN) { return(TestStatistics.SingleSuccess); } else { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action produced ", results[0].ToString(), "; expected a single canonical NaN.", Assembler.Highlight(expression))); return(TestStatistics.SingleFailure); } } else if (expression.IsCallTo("assert_return_arithmetic_nan")) { var results = RunAction(expression.Tail[0]); bool isNaN; if (results.Count != 1) { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action produced ", results.Count.ToString(), " results (", string.Join(", ", results), "); expected a single NaN.", Assembler.Highlight(expression))); return(TestStatistics.SingleFailure); } else if (results[0] is double) { isNaN = double.IsNaN((double)results[0]); } else if (results[0] is float) { isNaN = float.IsNaN((float)results[0]); } else { isNaN = false; } if (isNaN) { return(TestStatistics.SingleSuccess); } else { Log.Log( new LogEntry( Severity.Error, "assertion failed", "action produced ", results[0].ToString(), "; expected a single NaN.", Assembler.Highlight(expression))); return(TestStatistics.SingleFailure); } } else { Log.Log( new LogEntry( Severity.Warning, "unknown script command", Quotation.QuoteEvenInBold( "expression ", expression.Head.Span.Text, " was not recognized as a known script command."), Assembler.Highlight(expression))); return(TestStatistics.SingleUnknown); } }