Translate arith expressions using the new parser (#346)

This commit is contained in:
Ara Adkins 2019-11-20 09:18:53 +00:00 committed by GitHub
parent d1796345a4
commit b91ab25fdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 563 additions and 390 deletions

View File

@ -645,6 +645,7 @@ object AST {
}
object Text {
val any = UnapplyByType[Text]
//// Definition ////

View File

@ -0,0 +1,31 @@
package org.enso.syntax.text
/** This object contains view patterns that allow matching on the parser AST for
* more sophisticated constructs.
*
* These view patterns are implemented as custom unapply methods that only
* return [[Some]] when more complex conditions are met.
*/
object View {
object Assignment {
val assignmentOpSym = AST.Ident.Opr("=")
def unapply(ast: AST): Option[(AST, AST)] = {
ast match {
case AST.App.Infix.any(ast) => {
val left = ast.larg
val op = ast.opr
val right = ast.rarg
if (op == assignmentOpSym) {
Some((left, right))
} else {
None
}
}
case _ => None
}
}
}
}

View File

@ -14,7 +14,7 @@ class AtomFixtures extends InterpreterRunner {
|}
""".stripMargin
val generateList = eval(generateListCode)
val generateList = evalOld(generateListCode)
val millionElementList = generateList.call(million)
@ -30,7 +30,7 @@ class AtomFixtures extends InterpreterRunner {
|}
""".stripMargin
val reverseList = eval(reverseListCode)
val reverseList = evalOld(reverseListCode)
val reverseListMethodsCode =
"""
@ -43,7 +43,7 @@ class AtomFixtures extends InterpreterRunner {
|{ |list| @reverse [list, @Nil] }
|""".stripMargin
val reverseListMethods = eval(reverseListMethodsCode)
val reverseListMethods = evalOld(reverseListMethodsCode)
val sumListCode =
"""
@ -57,7 +57,7 @@ class AtomFixtures extends InterpreterRunner {
|}
""".stripMargin
val sumList = eval(sumListCode)
val sumList = evalOld(sumListCode)
val sumListLeftFoldCode =
"""
@ -72,7 +72,7 @@ class AtomFixtures extends InterpreterRunner {
|}
""".stripMargin
val sumListLeftFold = eval(sumListLeftFoldCode)
val sumListLeftFold = evalOld(sumListLeftFoldCode)
val sumListFallbackCode =
"""
@ -86,7 +86,7 @@ class AtomFixtures extends InterpreterRunner {
|}
""".stripMargin
val sumListFallback = eval(sumListFallbackCode)
val sumListFallback = evalOld(sumListFallbackCode)
val sumListMethodsCode =
"""
@ -98,7 +98,7 @@ class AtomFixtures extends InterpreterRunner {
|{ |list| @sum [list, 0] }
|""".stripMargin
val sumListMethods = eval(sumListMethodsCode)
val sumListMethods = evalOld(sumListMethodsCode)
val mapReverseListCode =
"""
@ -110,7 +110,7 @@ class AtomFixtures extends InterpreterRunner {
|{ |list| @mapReverse [list, { |x| x + 1 }, @Nil] }
|""".stripMargin
val mapReverseList = eval(mapReverseListCode)
val mapReverseList = evalOld(mapReverseListCode)
val mapReverseListCurryCode =
"""
@ -125,5 +125,5 @@ class AtomFixtures extends InterpreterRunner {
|}
|""".stripMargin
val mapReverseListCurry = eval(mapReverseListCurryCode)
val mapReverseListCurry = evalOld(mapReverseListCurryCode)
}

View File

@ -16,7 +16,7 @@ class NamedDefaultedArgumentFixtures extends InterpreterRunner {
|}
""".stripMargin
val sumTCOWithNamedArguments = eval(sumTCOWithNamedArgumentsCode)
val sumTCOWithNamedArguments = evalOld(sumTCOWithNamedArgumentsCode)
val sumTCOWithDefaultedArgumentsCode =
"""
@ -29,6 +29,6 @@ class NamedDefaultedArgumentFixtures extends InterpreterRunner {
|}
""".stripMargin
val sumTCOWithDefaultedArguments = eval(sumTCOWithDefaultedArgumentsCode)
val sumTCOWithDefaultedArguments = evalOld(sumTCOWithDefaultedArgumentsCode)
}

View File

@ -44,7 +44,7 @@ class RecursionFixtures extends InterpreterRunner {
|}
|""".stripMargin
val sumTCOFoldLike = eval(sumTCOFoldLikeCode)
val sumTCOFoldLike = evalOld(sumTCOFoldLikeCode)
val sumRecursiveCode =
"""
@ -82,7 +82,7 @@ class RecursionFixtures extends InterpreterRunner {
|}
|""".stripMargin
val sumStateTCO = eval(sumStateTCOCode)
val sumStateTCO = evalOld(sumStateTCOCode)
val sumTCOWithEvalCode =
"""
@ -94,5 +94,5 @@ class RecursionFixtures extends InterpreterRunner {
| res
|}
|""".stripMargin
val sumTCOWithEval = eval(sumTCOWithEvalCode)
val sumTCOWithEval = evalOld(sumTCOWithEvalCode)
}

View File

@ -20,4 +20,9 @@ public class Constants {
public static final String THUNK_EXECUTOR_NODE = "10";
public static final String EVAL_NODE = "10";
}
/** Constants used for debugging only. */
public static class Debug {
public static final String MIME_TYPE = "application/x-enso-old";
}
}

View File

@ -30,7 +30,7 @@ import org.graalvm.options.OptionDescriptors;
implementationName = Constants.IMPL_NAME,
version = Constants.LANGUAGE_VERSION,
defaultMimeType = Constants.MIME_TYPE,
characterMimeTypes = Constants.MIME_TYPE,
characterMimeTypes = {Constants.MIME_TYPE, Constants.Debug.MIME_TYPE},
contextPolicy = TruffleLanguage.ContextPolicy.SHARED,
fileTypeDetectors = FileDetector.class)
@ProvidedTags({

View File

@ -26,7 +26,8 @@ public final class FileDetector implements TruffleFile.FileTypeDetector {
public String findMimeType(TruffleFile file) throws IOException {
String name = file.getName();
if (name != null && name.endsWith(Constants.FILE_EXTENSION)) {
return Constants.MIME_TYPE;
// TODO [AA] Once the new connection is complete remove this
return Constants.Debug.MIME_TYPE;
}
return null;
}

View File

@ -2,13 +2,10 @@ package org.enso.compiler
import com.oracle.truffle.api.TruffleFile
import com.oracle.truffle.api.source.Source
import org.enso.compiler.generate.AstToIr
import org.enso.compiler.generate.AstToAstExpression
import org.enso.compiler.core.IR
import org.enso.flexer.Reader
import org.enso.interpreter.AstExpression
import org.enso.interpreter.Constants
import org.enso.interpreter.EnsoParser
import org.enso.interpreter.Language
import org.enso.interpreter.{AstExpression, AstModuleScope, Constants, EnsoParser, Language}
import org.enso.interpreter.builder.ExpressionFactory
import org.enso.interpreter.builder.ModuleScopeExpressionFactory
import org.enso.interpreter.node.ExpressionNode
@ -45,19 +42,18 @@ class Compiler(
* executable functionality in the module corresponding to `source`.
*/
def run(source: Source, scope: ModuleScope): ExpressionNode = {
/* TODO [AA] Introduce this next task
* val parsedAST: AST = parse(source)
* val convertedIR: ExpressionNode = translate(parsedAST)
*/
val mimeType = source.getMimeType
val parsed =
val expr: AstModuleScope = if (mimeType == Constants.MIME_TYPE) {
val parsedAST: AST = parse(source)
translate(parsedAST)
} else {
new EnsoParser().parseEnso(source.getCharacters.toString)
}
new ModuleScopeExpressionFactory(language, scope).run(parsed)
new ModuleScopeExpressionFactory(language, scope).run(expr)
}
// TODO [AA] This needs to evolve to support scope execution
/**
* Processes the language sources in the provided file, registering any
* bindings in the given scope.
@ -166,5 +162,6 @@ class Compiler(
* @return an IR representation with a 1:1 mapping to the parser AST
* constructs
*/
def translate(sourceAST: AST): IR = AstToIr.process(sourceAST)
def translate(sourceAST: AST): AstModuleScope =
AstToAstExpression.translate(sourceAST)
}

View File

@ -4,6 +4,7 @@ import org.enso.compiler.core.IR.Literal.Text
import org.enso.syntax.text.AST
import org.enso.syntax.text.ast.text.Escape
// TODO [AA] REMOVE THIS ENTIRE FILE ONCE WE HAVE NEW CORE
/**
* This is the compiler's high-level intermediate representation.
*

View File

@ -0,0 +1,374 @@
package org.enso.compiler.generate
import org.enso.compiler.exception.UnhandledEntity
import org.enso.compiler.core.IR
import org.enso.interpreter.{
AstArithOp,
AstExpression,
AstImport,
AstLong,
AstModuleScope,
AstModuleSymbol
}
import org.enso.syntax.text.{AST, Debug}
// TODO [AA] Please note that this entire translation is _very_ work-in-progress
// and is hence quite ugly right now. It will be cleaned up as work progresses,
// but it was thought best to land in increments where possible.
/**
* This is a representation of the raw conversion from the Parser [[AST AST]]
* to the internal [[IR IR]] used by the static transformation passes.
*/
object AstToAstExpression {
/**
* Transforms the input [[AST]] into the compiler's high-level intermediate
* representation.
*
* @param inputAST the AST to transform
* @return a representation of the program construct represented by
* `inputAST` in the compiler's [[IR IR]]
*/
def translate(inputAST: AST): AstModuleScope = {
// println(Debug.pretty(inputAST.toString))
// println("=========================================")
inputAST match {
case AST.Module.any(inputAST) => translateModule(inputAST)
case _ => {
throw new UnhandledEntity(inputAST, "process")
}
}
}
def translateModuleSymbol(inputAST: AST): AstModuleSymbol = {
???
}
def translateLiteral(literal: AST.Literal): AstLong = {
literal match {
case AST.Literal.Number(base, number) => {
if (base.isDefined && base.get != "10") {
throw new RuntimeException("Only base 10 is currently supported")
}
AstLong(number.toLong)
}
// case AST.Literal.Text.any(literal) =>
case _ => throw new UnhandledEntity(literal, "processLiteral")
}
}
def translateApplication(application: AST): AstExpression = {
application match {
case AST.App.Infix(left, fn, right) => {
// FIXME [AA] We should accept all ops when translating to core
val validInfixOps = List("+", "/", "-", "*", "%")
if (validInfixOps.contains(fn.name)) {
AstArithOp(
fn.name,
translateExpression(left),
translateExpression(right)
)
} else {
throw new RuntimeException(
s"${fn.name} is not currently a valid infix operator"
)
}
}
// case AST.App.Prefix(fn, arg) =>
// case AST.App.Section.any(application) => // TODO [AA] left, sides, right
// case AST.Mixfix(application) => // TODO [AA] translate if
case _ => throw new UnhandledEntity(application, "translateApplication")
}
}
def translateExpression(inputAST: AST): AstExpression = {
inputAST match {
case AST.App.any(inputAST) => translateApplication(inputAST)
case AST.Literal.any(inputAST) => translateLiteral(inputAST)
case AST.Group.any(inputAST) => translateGroup(inputAST)
case _ =>
throw new UnhandledEntity(inputAST, "translateExpression")
}
// inputAST match {
// case AST.App.any(inputAST) => processApplication(inputAST)
// case AST.Block.any(inputAST) => processBlock(inputAST)
// case AST.Comment.any(inputAST) => processComment(inputAST)
// case AST.Ident.any(inputAST) => processIdent(inputAST)
// case AST.Import.any(inputAST) => processBinding(inputAST)
// case AST.Invalid.any(inputAST) => processInvalid(inputAST)
// case AST.Literal.any(inputAST) => processLiteral(inputAST)
// case AST.Mixfix.any(inputAST) => processApplication(inputAST)
// case AST.Group.any(inputAST) => processGroup(inputAST)
// case AST.Def.any(inputAST) => processBinding(inputAST)
// case AST.Foreign.any(inputAST) => processBlock(inputAST)
// case _ =>
// IR.Error.UnhandledAST(inputAST)
// }
}
def translateGroup(group: AST.Group): AstExpression = {
group.body match {
case Some(ast) => translateExpression(ast)
case None => {
// FIXME [AA] This should generate an error node in core
throw new RuntimeException("Empty group")
}
}
}
// TODO [AA] Fix the types
def translateModule(module: AST.Module): AstModuleScope = {
module match {
case AST.Module(blocks) => {
val presentBlocks = blocks.collect {
case t if t.elem.isDefined => t.elem.get
}
val imports = presentBlocks.collect {
case AST.Import.any(list) => translateImport(list)
}
val nonImportBlocks = presentBlocks.filter {
case AST.Import.any(_) => false
case _ => true
}
if (nonImportBlocks.isEmpty) {
// FIXME [AA] This is temporary, and should be moved to generate an
// error node in Core.
throw new RuntimeException("Cannot have no expressions")
}
val statements = nonImportBlocks.dropRight(1).map(translateModuleSymbol)
val expression = translateExpression(nonImportBlocks.last)
AstModuleScope(imports, statements, expression)
}
}
}
def translateImport(imp: AST.Import): AstImport = {
AstImport(imp.path.map(t => t.name).reduceLeft((l, r) => l + "." + r))
}
/**
* Transforms invalid entities from the parser AST.
*
* @param invalid the invalid entity
* @return a representation of `invalid` in the compiler's [[IR IR]]
*/
def processInvalid(invalid: AST.Invalid): IR.Error = {
???
// invalid match {
// case AST.Invalid.Unexpected(str, unexpectedTokens) =>
// IR.Error.UnexpectedToken(str, unexpectedTokens.map(t => process(t.el)))
// case AST.Invalid.Unrecognized(str) => IR.Error.UnrecognisedSymbol(str)
// case AST.Ident.InvalidSuffix(identifier, suffix) =>
// IR.Error.InvalidSuffix(processIdent(identifier), suffix)
// case AST.Literal.Text.Unclosed(AST.Literal.Text.Line.Raw(text)) =>
// IR.Error.UnclosedText(List(processLine(text)))
// case AST.Literal.Text.Unclosed(AST.Literal.Text.Line.Fmt(text)) =>
// IR.Error.UnclosedText(List(processLine(text)))
// case _ =>
// throw new RuntimeException(
// "Fatal: Unhandled entity in processInvalid = " + invalid
// )
// }
}
/**
* Transforms identifiers from the parser AST.
*
* @param identifier the identifier
* @return a representation of `identifier` in the compiler's [[IR IR]]
*/
def processIdent(identifier: AST.Ident): IR.Identifier = {
???
// identifier match {
// case AST.Ident.Blank(_) => IR.Identifier.Blank()
// case AST.Ident.Var(name) => IR.Identifier.Variable(name)
// case AST.Ident.Cons.any(identifier) => processIdentConstructor(identifier)
// case AST.Ident.Opr.any(identifier) => processIdentOperator(identifier)
// case AST.Ident.Mod(name) => IR.Identifier.Module(name)
// case _ =>
// throw new RuntimeException(
// "Fatal: Unhandled entity in processIdent = " + identifier
// )
// }
}
/**
* Transforms an operator identifier from the parser AST.
*
* @param operator the operator to transform
* @return a representation of `operator` in the compiler's [[IR IR]]
*/
def processIdentOperator(
operator: AST.Ident.Opr
): IR.Identifier.Operator = {
???
// IR.Identifier.Operator(operator.name)
}
/**
* Transforms a constructor identifier from the parser AST.
*
* @param constructor the constructor name to transform
* @return a representation of `constructor` in the compiler's [[IR IR]]
*/
def processIdentConstructor(
constructor: AST.Ident.Cons
): IR.Identifier.Constructor = {
???
// IR.Identifier.Constructor(constructor.name)
}
/**
* Transforms a line of a text literal from the parser AST.
*
* @param line the literal line to transform
* @return a representation of `line` in the compiler's [[IR IR]]
*/
def processLine(
line: List[AST.Literal.Text.Segment[AST]]
): IR.Literal.Text.Line = {
???
// IR.Literal.Text.Line(line.map(processTextSegment))
}
/**
* Transforms a segment of text from the parser AST.
*
* @param segment the text segment to transform
* @return a representation of `segment` in the compiler's [[IR IR]]
*/
def processTextSegment(
segment: AST.Literal.Text.Segment[AST]
): IR.Literal.Text.Segment = {
???
// segment match {
// case AST.Literal.Text.Segment._Plain(str) =>
// IR.Literal.Text.Segment.Plain(str)
// case AST.Literal.Text.Segment._Expr(expr) =>
// IR.Literal.Text.Segment.Expression(expr.map(process))
// case AST.Literal.Text.Segment._Escape(code) =>
// IR.Literal.Text.Segment.EscapeCode(code)
// case _ => throw new UnhandledEntity(segment, "processTextSegment")
// }
}
/**
* Transforms a function application from the parser AST.
*
* @param application the function application to transform
* @return a representation of `application` in the compiler's [[IR IR]]
*/
def processApplication(application: AST): IR.Application = {
???
// application match {
// case AST.App.Prefix(fn, arg) =>
// IR.Application.Prefix(process(fn), process(arg))
// case AST.App.Infix(leftArg, fn, rightArg) =>
// IR.Application.Infix(
// process(leftArg),
// processIdentOperator(fn),
// process(rightArg)
// )
// case AST.App.Section.Left(arg, fn) =>
// IR.Application.Section.Left(process(arg), processIdentOperator(fn))
// case AST.App.Section.Right(fn, arg) =>
// IR.Application.Section.Right(processIdentOperator(fn), process(arg))
// case AST.App.Section.Sides(fn) =>
// IR.Application.Section.Sides(processIdentOperator(fn))
// case AST.Mixfix(fnSegments, args) =>
// IR.Application
// .Mixfix(fnSegments.toList.map(processIdent), args.toList.map(process))
// case _ =>
// throw new UnhandledEntity(application, "processApplication")
// }
}
/**
* Transforms a source code block from the parser AST.
*
* This handles both blocks of Enso-native code, and blocks of foreign
* language code.
*
* @param block the block to transform
* @return a representation of `block` in the compiler's [[IR IR]]
*/
def processBlock(block: AST): IR.Block = {
???
// block match {
// case AST.Block(_, _, firstLine, lines) =>
// IR.Block
// .Enso(
// process(firstLine.elem) ::
// lines.filter(t => t.elem.isDefined).map(t => process(t.elem.get))
// )
// case AST.Foreign(_, language, code) => IR.Block.Foreign(language, code)
// case _ => throw new UnhandledEntity(block, "processBlock")
// }
}
/**
* Transforms a module top-level from the parser AST.
*
* @param module the module to transform
* @return a representation of `module` in the compiler's [[IR IR]]
*/
def processModule(module: AST.Module): IR.Module = {
???
// module match {
// case AST.Module(lines) =>
// IR.Module(
// lines.filter(t => t.elem.isDefined).map(t => process(t.elem.get))
// )
// case _ => throw new UnhandledEntity(module, "processModule")
// }
}
/**
* Transforms a comment from the parser AST.
*
* @param comment the comment to transform
* @return a representation of `comment` in the compiler's [[IR IR]]
*/
def processComment(comment: AST): IR.Comment = {
???
// comment match {
// case AST.Comment(lines) => IR.Comment(lines)
// case _ => throw new UnhandledEntity(comment, "processComment")
// }
}
/**
* Transforms a binding from the parser AST.
*
* Bindings are any constructs that give some Enso language construct a name.
* This includes type definitions, imports, assignments, and so on.
*
* @param binding the binding to transform
* @return a representation of `binding` in the compiler's [[IR IR]]
*/
def processBinding(binding: AST): IR.Binding = {
???
// binding match {
// case AST.Def(constructor, arguments, optBody) =>
// IR.Binding.RawType(
// processIdentConstructor(constructor),
// arguments.map(process),
// optBody.map(process)
// )
// case AST.Import(components) => {
// IR.Binding.Import(
// components.toList.map(t => processIdentConstructor(t))
// )
// }
// case _ => throw new UnhandledEntity(binding, "processBinding")
// }
}
}

View File

@ -1,264 +0,0 @@
package org.enso.compiler.generate
import org.enso.compiler.exception.UnhandledEntity
import org.enso.compiler.core.IR
import org.enso.syntax.text.AST
/**
* This is a representation of the raw conversion from the Parser [[AST AST]]
* to the internal [[IR IR]] used by the static transformation passes.
*/
object AstToIr {
/**
* Transforms the input [[AST]] into the compiler's high-level intermediate
* representation.
*
* @param inputAST the AST to transform
* @return a representation of the program construct represented by
* `inputAST` in the compiler's [[IR IR]]
*/
def process(inputAST: AST): IR = inputAST match {
case AST.App.any(inputAST) => processApplication(inputAST)
case AST.Block.any(inputAST) => processBlock(inputAST)
case AST.Comment.any(inputAST) => processComment(inputAST)
case AST.Ident.any(inputAST) => processIdent(inputAST)
case AST.Import.any(inputAST) => processBinding(inputAST)
case AST.Invalid.any(inputAST) => processInvalid(inputAST)
case AST.Literal.any(inputAST) => processLiteral(inputAST)
case AST.Mixfix.any(inputAST) => processApplication(inputAST)
case AST.Module.any(inputAST) => processModule(inputAST)
case AST.Group.any(inputAST) => processGroup(inputAST)
case AST.Def.any(inputAST) => processBinding(inputAST)
case AST.Foreign.any(inputAST) => processBlock(inputAST)
case _ =>
IR.Error.UnhandledAST(inputAST)
}
/**
* Transforms invalid entities from the parser AST.
*
* @param invalid the invalid entity
* @return a representation of `invalid` in the compiler's [[IR IR]]
*/
def processInvalid(invalid: AST.Invalid): IR.Error = invalid match {
case AST.Invalid.Unexpected(str, unexpectedTokens) =>
IR.Error.UnexpectedToken(str, unexpectedTokens.map(t => process(t.el)))
case AST.Invalid.Unrecognized(str) => IR.Error.UnrecognisedSymbol(str)
case AST.Ident.InvalidSuffix(identifier, suffix) =>
IR.Error.InvalidSuffix(processIdent(identifier), suffix)
case AST.Literal.Text.Unclosed(AST.Literal.Text.Line.Raw(text)) =>
IR.Error.UnclosedText(List(processLine(text)))
case AST.Literal.Text.Unclosed(AST.Literal.Text.Line.Fmt(text)) =>
IR.Error.UnclosedText(List(processLine(text)))
case _ =>
throw new RuntimeException(
"Fatal: Unhandled entity in processInvalid = " + invalid
)
}
/**
* Transforms identifiers from the parser AST.
*
* @param identifier the identifier
* @return a representation of `identifier` in the compiler's [[IR IR]]
*/
def processIdent(identifier: AST.Ident): IR.Identifier = identifier match {
case AST.Ident.Blank(_) => IR.Identifier.Blank()
case AST.Ident.Var(name) => IR.Identifier.Variable(name)
case AST.Ident.Cons.any(identifier) => processIdentConstructor(identifier)
case AST.Ident.Opr.any(identifier) => processIdentOperator(identifier)
case AST.Ident.Mod(name) => IR.Identifier.Module(name)
case _ =>
throw new RuntimeException(
"Fatal: Unhandled entity in processIdent = " + identifier
)
}
/**
* Transforms an operator identifier from the parser AST.
*
* @param operator the operator to transform
* @return a representation of `operator` in the compiler's [[IR IR]]
*/
def processIdentOperator(
operator: AST.Ident.Opr
): IR.Identifier.Operator = IR.Identifier.Operator(operator.name)
/**
* Transforms a constructor identifier from the parser AST.
*
* @param constructor the constructor name to transform
* @return a representation of `constructor` in the compiler's [[IR IR]]
*/
def processIdentConstructor(
constructor: AST.Ident.Cons
): IR.Identifier.Constructor = IR.Identifier.Constructor(constructor.name)
/**
* Transforms a literal from the parser AST.
*
* @param literal the literal to transform
* @return a representation of `literal` in the compiler's [[IR IR]]
*/
def processLiteral(literal: AST.Literal): IR.Literal = {
literal match {
case AST.Literal.Number(base, number) => IR.Literal.Number(number, base)
// TODO [AA] Handle text properly
// case AST.Literal.Text.Raw(body) =>
// IR.Literal.Text.Raw(body.lines.toList.map(processLine))
//
// case AST.Literal.Text.Line.Fmt(lines) =>
// IR.Literal.Text.Format(lines.toList.map(processLine))
case _ => throw new UnhandledEntity(literal, "processLiteral")
}
}
/**
* Transforms a line of a text literal from the parser AST.
*
* @param line the literal line to transform
* @return a representation of `line` in the compiler's [[IR IR]]
*/
def processLine(
line: List[AST.Literal.Text.Segment[AST]]
): IR.Literal.Text.Line =
IR.Literal.Text.Line(line.map(processTextSegment))
/**
* Transforms a segment of text from the parser AST.
*
* @param segment the text segment to transform
* @return a representation of `segment` in the compiler's [[IR IR]]
*/
def processTextSegment(
segment: AST.Literal.Text.Segment[AST]
): IR.Literal.Text.Segment = segment match {
case AST.Literal.Text.Segment._Plain(str) =>
IR.Literal.Text.Segment.Plain(str)
case AST.Literal.Text.Segment._Expr(expr) =>
IR.Literal.Text.Segment.Expression(expr.map(process))
case AST.Literal.Text.Segment._Escape(code) =>
IR.Literal.Text.Segment.EscapeCode(code)
case _ => throw new UnhandledEntity(segment, "processTextSegment")
}
/**
* Transforms a function application from the parser AST.
*
* @param application the function application to transform
* @return a representation of `application` in the compiler's [[IR IR]]
*/
def processApplication(application: AST): IR.Application =
application match {
case AST.App.Prefix(fn, arg) =>
IR.Application.Prefix(process(fn), process(arg))
case AST.App.Infix(leftArg, fn, rightArg) =>
IR.Application.Infix(
process(leftArg),
processIdentOperator(fn),
process(rightArg)
)
case AST.App.Section.Left(arg, fn) =>
IR.Application.Section.Left(process(arg), processIdentOperator(fn))
case AST.App.Section.Right(fn, arg) =>
IR.Application.Section.Right(processIdentOperator(fn), process(arg))
case AST.App.Section.Sides(fn) =>
IR.Application.Section.Sides(processIdentOperator(fn))
case AST.Mixfix(fnSegments, args) =>
IR.Application
.Mixfix(fnSegments.toList.map(processIdent), args.toList.map(process))
case _ =>
throw new UnhandledEntity(application, "processApplication")
}
/**
* Transforms a source code block from the parser AST.
*
* This handles both blocks of Enso-native code, and blocks of foreign
* language code.
*
* @param block the block to transform
* @return a representation of `block` in the compiler's [[IR IR]]
*/
def processBlock(block: AST): IR.Block = block match {
case AST.Block(_, _, firstLine, lines) =>
IR.Block
.Enso(
process(firstLine.elem) ::
lines.filter(t => t.elem.isDefined).map(t => process(t.elem.get))
)
case AST.Foreign(_, language, code) => IR.Block.Foreign(language, code)
case _ => throw new UnhandledEntity(block, "processBlock")
}
/**
* Transforms a module top-level from the parser AST.
*
* @param module the module to transform
* @return a representation of `module` in the compiler's [[IR IR]]
*/
def processModule(module: AST.Module): IR.Module = module match {
case AST.Module(lines) =>
IR.Module(
lines.filter(t => t.elem.isDefined).map(t => process(t.elem.get))
)
case _ => throw new UnhandledEntity(module, "processModule")
}
/**
* Transforms a comment from the parser AST.
*
* @param comment the comment to transform
* @return a representation of `comment` in the compiler's [[IR IR]]
*/
def processComment(comment: AST): IR.Comment = comment match {
case AST.Comment(lines) => IR.Comment(lines)
case _ => throw new UnhandledEntity(comment, "processComment")
}
/**
* Transforms a group from the parser AST.
*
* In [[IR]], groups are actually non-entities, as all grouping is handled
* implicitly by the IR format. A valid group is replaced by its contents in
* the IR, while invalid groups are replaced by error nodes.
*
* @param group the group to transform
* @return a representation of `group` in the compiler's [[IR IR]]
*/
def processGroup(group: AST): IR = group match {
case AST.Group(maybeAST) =>
maybeAST match {
case Some(ast) => process(ast)
case None => IR.Error.EmptyGroup()
}
case _ => throw new UnhandledEntity(group, "processGroup")
}
/**
* Transforms a binding from the parser AST.
*
* Bindings are any constructs that give some Enso language construct a name.
* This includes type definitions, imports, assignments, and so on.
*
* @param binding the binding to transform
* @return a representation of `binding` in the compiler's [[IR IR]]
*/
def processBinding(binding: AST): IR.Binding = binding match {
case AST.Def(constructor, arguments, optBody) =>
IR.Binding.RawType(
processIdentConstructor(constructor),
arguments.map(process),
optBody.map(process)
)
case AST.Import(components) => {
IR.Binding.Import(
components.toList.map(t => processIdentConstructor(t))
)
}
case _ => throw new UnhandledEntity(binding, "processBinding")
}
}

View File

@ -59,21 +59,21 @@ trait AstModuleScopeVisitor[+T] {
): T
}
sealed trait AstGlobalSymbol
sealed trait AstModuleSymbol
case class AstTypeDef(name: String, arguments: List[AstArgDefinition])
extends AstGlobalSymbol {
extends AstModuleSymbol {
def getArguments: java.util.List[AstArgDefinition] = arguments.asJava
}
case class AstMethodDef(typeName: String, methodName: String, fun: AstFunction)
extends AstGlobalSymbol
extends AstModuleSymbol
case class AstImport(name: String)
case class AstModuleScope(
imports: List[AstImport],
bindings: List[AstGlobalSymbol],
bindings: List[AstModuleSymbol],
expression: AstExpression
) {
@ -330,7 +330,7 @@ class EnsoParserInternal extends JavaTokenParsers {
def statement: Parser[AstExpression] = assignment | expression
def typeDef: Parser[AstGlobalSymbol] =
def typeDef: Parser[AstModuleSymbol] =
"type" ~> ident ~ ((argDefinition | ("(" ~> argDefinition <~ ")")) *) <~ ";" ^^ {
case name ~ args => AstTypeDef(name, args)
}

View File

@ -1,10 +1,10 @@
package org.enso.interpreter.test
import java.io.ByteArrayOutputStream
import java.io.{ByteArrayOutputStream, StringReader}
import org.enso.interpreter.Constants
import org.graalvm.polyglot.{Context, Source, Value}
import org.enso.interpreter.instrument.ReplDebuggerInstrument
import org.graalvm.polyglot.{Context, Value}
import org.scalatest.{FlatSpec, Matchers}
trait InterpreterRunner {
@ -17,9 +17,23 @@ trait InterpreterRunner {
val output = new ByteArrayOutputStream()
val ctx = Context.newBuilder(Constants.LANGUAGE_ID).out(output).build()
def eval(code: String): Value = {
def evalGeneric(code: String, mimeType: String): Value = {
output.reset()
InterpreterException.rethrowPolyglot(ctx.eval(Constants.LANGUAGE_ID, code))
val source = Source
.newBuilder(Constants.LANGUAGE_ID, new StringReader(code), "test")
.mimeType(mimeType)
.build()
InterpreterException.rethrowPolyglot(ctx.eval(source))
}
def eval(code: String): Value = {
evalGeneric(code, Constants.MIME_TYPE)
}
def evalOld(code: String): Value = {
evalGeneric(code, Constants.Debug.MIME_TYPE)
}
def consumeOut: List[String] = {
@ -29,7 +43,7 @@ trait InterpreterRunner {
}
def parse(code: String): Value =
InterpreterException.rethrowPolyglot(eval(code))
InterpreterException.rethrowPolyglot(evalOld(code))
def getReplInstrument: ReplDebuggerInstrument = {
ctx.getEngine.getInstruments

View File

@ -19,7 +19,7 @@ class ReplTest extends InterpreterTest {
scopeResult = executor.listBindings.asScala.toMap
executor.exit()
}
eval(code)
evalOld(code)
scopeResult shouldEqual Map("x" -> 10, "y" -> 20, "z" -> 30)
}
@ -37,7 +37,7 @@ class ReplTest extends InterpreterTest {
evalResult = executor.evaluate("x + y")
executor.exit()
}
eval(code)
evalOld(code)
evalResult shouldEqual 3
}
@ -55,7 +55,7 @@ class ReplTest extends InterpreterTest {
executor.evaluate("a + b")
executor.exit()
}
eval(code) shouldEqual 55
evalOld(code) shouldEqual 55
}
"Repl" should "be able to define its local variables" in {
@ -72,7 +72,7 @@ class ReplTest extends InterpreterTest {
executor.evaluate("z")
executor.exit()
}
eval(code) shouldEqual 110
evalOld(code) shouldEqual 110
}
"Repl" should "access and modify monadic state" in {
@ -89,6 +89,6 @@ class ReplTest extends InterpreterTest {
executor.evaluate("@put[@State, x+1]")
executor.exit()
}
eval(code) shouldEqual 11
evalOld(code) shouldEqual 11
}
}

View File

@ -15,7 +15,7 @@ class ConstructorsTest extends InterpreterTest {
| >
|}
""".stripMargin
eval(patternMatchingCode) shouldEqual 1
evalOld(patternMatchingCode) shouldEqual 1
}
"Recursion with pattern matching" should "terminate" in {
@ -31,7 +31,7 @@ class ConstructorsTest extends InterpreterTest {
| res
|}
""".stripMargin
eval(testCode) shouldEqual 55
evalOld(testCode) shouldEqual 55
}
"Pattern match expression" should "behave correctly in non-tail positions" in {
@ -46,7 +46,7 @@ class ConstructorsTest extends InterpreterTest {
| result + 1
|}
""".stripMargin
eval(testCode).execute() shouldEqual 4
evalOld(testCode).execute() shouldEqual 4
}
"Pattern match expressions" should "accept a catch-all fallback clause" in {
@ -60,7 +60,7 @@ class ConstructorsTest extends InterpreterTest {
| >
|}
""".stripMargin
eval(testCode).execute() shouldEqual 1
evalOld(testCode).execute() shouldEqual 1
}
"Pattern match expressions" should "throw an exception when match fails" in {
@ -73,7 +73,7 @@ class ConstructorsTest extends InterpreterTest {
| >
|}
""".stripMargin
the[InterpreterException] thrownBy eval(testCode)
the[InterpreterException] thrownBy evalOld(testCode)
.call() should have message "Inexhaustive pattern match."
}
@ -93,6 +93,6 @@ class ConstructorsTest extends InterpreterTest {
|
|@sumList [@Unit, @genList [@Unit, 10]]
""".stripMargin
eval(testCode) shouldEqual 55
evalOld(testCode) shouldEqual 55
}
}

View File

@ -15,7 +15,7 @@ class CurryingTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 11
evalOld(code) shouldEqual 11
}
"Functions" should "allow default arguments to be suspended" in {
@ -32,7 +32,7 @@ class CurryingTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 26
evalOld(code) shouldEqual 26
}
"Functions" should "allow defaults to be suspended in application chains" in {
@ -45,6 +45,6 @@ class CurryingTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 32
evalOld(code) shouldEqual 32
}
}

View File

@ -16,7 +16,7 @@ class ErrorsTest extends InterpreterTest {
|}
|""".stripMargin
val exception = the[InterpreterException] thrownBy eval(code)
val exception = the[InterpreterException] thrownBy evalOld(code)
exception.isGuestException shouldEqual true
exception.getGuestObject.toString shouldEqual "Bar<>"
consumeOut shouldEqual List("Foo<>")
@ -34,7 +34,7 @@ class ErrorsTest extends InterpreterTest {
|}
|""".stripMargin
noException shouldBe thrownBy(eval(code))
noException shouldBe thrownBy(evalOld(code))
consumeOut shouldEqual List("Error:MyError<>")
}
@ -52,8 +52,8 @@ class ErrorsTest extends InterpreterTest {
| @println [@IO, matched]
|}
|""".stripMargin
eval(code)
noException shouldBe thrownBy(eval(code))
evalOld(code)
noException shouldBe thrownBy(evalOld(code))
consumeOut shouldEqual List("Error:MyError<>")
}
@ -65,7 +65,7 @@ class ErrorsTest extends InterpreterTest {
| @catch [intError, { |x| x + 3 }]
|}
|""".stripMargin
eval(code) shouldEqual 4
evalOld(code) shouldEqual 4
}
"Catch function" should "accept a constructor handler" in {
@ -78,7 +78,7 @@ class ErrorsTest extends InterpreterTest {
| @println [@IO, @catch [unitErr, MyCons]]
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("MyCons<Unit<>>")
}
@ -98,12 +98,12 @@ class ErrorsTest extends InterpreterTest {
|}
|
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("MyRecovered<20>")
}
"Catch function" should "act as identity for non-error values" in {
val code = "@catch [10, {|x| x + 1}]"
eval(code) shouldEqual 10
evalOld(code) shouldEqual 10
}
}

View File

@ -10,7 +10,7 @@ class EvalTest extends InterpreterTest {
| @eval [@Debug, "@println[@IO, \"foo\"]"]
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("foo")
}
@ -22,7 +22,7 @@ class EvalTest extends InterpreterTest {
| @eval [@Debug, "@println[@IO, x]"]
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("Hello World!")
}
@ -36,7 +36,7 @@ class EvalTest extends InterpreterTest {
| @eval [@Debug, "@println[@IO, @MyType[x]]"]
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("MyType<10>")
}
@ -50,7 +50,7 @@ class EvalTest extends InterpreterTest {
| res + 1
|}
|""".stripMargin
eval(code) shouldEqual 4
evalOld(code) shouldEqual 4
}
"Debug.eval" should "work in a recursive setting" in {
@ -64,7 +64,7 @@ class EvalTest extends InterpreterTest {
| res
|}
|""".stripMargin
val fun = eval(code)
val fun = evalOld(code)
fun.call(100) shouldEqual 5050
}
@ -79,7 +79,7 @@ class EvalTest extends InterpreterTest {
| res
|}
|""".stripMargin
val fun = eval(code)
val fun = evalOld(code)
fun.call(100) shouldEqual 5050
}
}

View File

@ -9,7 +9,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|{ |x| x * x }
|""".stripMargin
val function = eval(code)
val function = evalOld(code)
function.call(1) shouldEqual 1
function.call(4) shouldEqual 16
}
@ -25,7 +25,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|}
""".stripMargin
eval(code).call(3) shouldEqual 5
evalOld(code).call(3) shouldEqual 5
}
"Recursion" should "work" in {
@ -37,7 +37,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|}
""".stripMargin
eval(code) shouldEqual 55
evalOld(code) shouldEqual 55
}
"Function calls" should "accept more arguments than needed and pass them to the result upon execution" in {
@ -50,7 +50,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 3
evalOld(code) shouldEqual 3
}
"Function calls" should "allow oversaturation and execute until completion" in {
@ -63,7 +63,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 20
evalOld(code) shouldEqual 20
}
"Function calls" should "be able to return atoms that are evaluated with oversaturated args" in {
@ -80,7 +80,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 5
evalOld(code) shouldEqual 5
}
"Methods" should "support the use of oversaturated args" in {
@ -96,7 +96,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 1
evalOld(code) shouldEqual 1
}
"Recursion closing over lexical scope" should "work properly" in {
@ -109,6 +109,6 @@ class FunctionArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 0
evalOld(code) shouldEqual 0
}
}

View File

@ -12,7 +12,7 @@ class GlobalScopeTest extends InterpreterTest {
|@a [@Unit]
""".stripMargin
eval(code) shouldEqual 10
evalOld(code) shouldEqual 10
}
"Functions" should "use values from the global scope in their bodies" in {
@ -24,7 +24,7 @@ class GlobalScopeTest extends InterpreterTest {
|@addTen [@Unit, 5]
""".stripMargin
eval(code) shouldEqual 15
evalOld(code) shouldEqual 15
}
"Functions" should "be able to call other functions in scope" in {
@ -39,7 +39,7 @@ class GlobalScopeTest extends InterpreterTest {
|} [2]
""".stripMargin
eval(code) shouldEqual 6
evalOld(code) shouldEqual 6
}
"Functions" should "be able to be passed as values when in scope" in {
@ -55,7 +55,7 @@ class GlobalScopeTest extends InterpreterTest {
|@binaryFn [@Unit, 1, 2, { |a, b| @adder [@Unit, a, b] }]
""".stripMargin
eval(code) shouldEqual 3
evalOld(code) shouldEqual 3
}
"Functions" should "be able to mutually recurse in the global scope" in {
@ -73,7 +73,7 @@ class GlobalScopeTest extends InterpreterTest {
|@fn1 [@Unit, 5]
""".stripMargin
eval(code) shouldEqual 3
evalOld(code) shouldEqual 3
}
"Functions" should "be suspended within blocks" in {
@ -85,7 +85,7 @@ class GlobalScopeTest extends InterpreterTest {
|b
""".stripMargin
noException should be thrownBy eval(code)
noException should be thrownBy evalOld(code)
}
"Exceptions" should "be thrown when called" in {
@ -97,7 +97,7 @@ class GlobalScopeTest extends InterpreterTest {
|@b [@Unit]
""".stripMargin
an[InterpreterException] should be thrownBy eval(code)
an[InterpreterException] should be thrownBy evalOld(code)
}
}

View File

@ -12,7 +12,7 @@ class InteropTest extends InterpreterTest {
|}
|""".stripMargin
val recurFun = eval(code)
val recurFun = evalOld(code)
recurFun.call(15) shouldEqual 0
}
@ -25,7 +25,7 @@ class InteropTest extends InterpreterTest {
|}
|""".stripMargin
val curriedFun = eval(code)
val curriedFun = evalOld(code)
curriedFun.call(2, 3) shouldEqual 6
}
@ -35,7 +35,7 @@ class InteropTest extends InterpreterTest {
|{ |x, y, z| (x + y) + z }
|""".stripMargin
val fun = eval(code)
val fun = evalOld(code)
fun.call(1).call(2).call(3) shouldEqual 6
}
@ -47,7 +47,7 @@ class InteropTest extends InterpreterTest {
|{ |x| method }
|""".stripMargin
val fun = eval(code)
val fun = evalOld(code)
fun.call(1, 2) shouldEqual 2
}
}

View File

@ -14,7 +14,7 @@ class LazyArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
noException should be thrownBy parse(code)
eval(code)
evalOld(code)
consumeOut shouldEqual List("2")
}
@ -28,7 +28,7 @@ class LazyArgumentsTest extends InterpreterTest {
| res
|}
|""".stripMargin
eval(code) shouldEqual 50005000
evalOld(code) shouldEqual 50005000
}
subject should "work in non-tail positions" in {
@ -41,7 +41,7 @@ class LazyArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
val result = eval(code)
val result = evalOld(code)
result shouldEqual 12
}
@ -60,7 +60,7 @@ class LazyArgumentsTest extends InterpreterTest {
| @method [@Foo, @println [@IO, 3]]
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("2")
}
@ -74,7 +74,7 @@ class LazyArgumentsTest extends InterpreterTest {
| @foo [1, @println [@IO, 3], @println [@IO, 4]]
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("1","4")
}
}

View File

@ -15,7 +15,7 @@ class LexicalScopeTest extends InterpreterTest {
|}
""".stripMargin
eval(code) shouldEqual 15
evalOld(code) shouldEqual 15
}
"Variable shadowing" should "work" in {
@ -29,7 +29,7 @@ class LexicalScopeTest extends InterpreterTest {
| }
|}
""".stripMargin
eval(code) shouldEqual 6
evalOld(code) shouldEqual 6
}
"Variable redefinition in same scope" should "throw error" in {
@ -44,7 +44,7 @@ class LexicalScopeTest extends InterpreterTest {
| }
|}
""".stripMargin
the[InterpreterException] thrownBy eval(code) should have message "Variable y was already defined in this scope."
the[InterpreterException] thrownBy evalOld(code) should have message "Variable y was already defined in this scope."
}
"Reference to an undefined variable" should "throw error" in {
@ -58,7 +58,7 @@ class LexicalScopeTest extends InterpreterTest {
| y
|}
""".stripMargin
the[InterpreterException] thrownBy eval(code) should have message "Variable y is not defined."
the[InterpreterException] thrownBy evalOld(code) should have message "Variable y is not defined."
}
}

View File

@ -10,7 +10,7 @@ class MethodsTest extends InterpreterTest {
|Foo.bar = { |number| number + 1 }
|@bar [@Foo, 10]
|""".stripMargin
eval(code) shouldEqual 11
evalOld(code) shouldEqual 11
}
"Methods" should "be dispatched to the proper constructor" in {
@ -24,7 +24,7 @@ class MethodsTest extends InterpreterTest {
|@sum [@Cons [1, @Cons [2, @Nil]], 0]
|""".stripMargin
eval(code) shouldEqual 3
evalOld(code) shouldEqual 3
}
"Method call target" should "be passable by-name" in {
@ -34,7 +34,7 @@ class MethodsTest extends InterpreterTest {
|@testMethod [x = 1, y = 2, this = @Unit, z = 3]
|""".stripMargin
eval(code) shouldEqual 6
evalOld(code) shouldEqual 6
}
"Calling a non-existent method" should "throw an exception" in {
@ -42,7 +42,7 @@ class MethodsTest extends InterpreterTest {
"""
|@foo [7]
|""".stripMargin
the[InterpreterException] thrownBy eval(code) should have message "Object Number does not define method foo."
the[InterpreterException] thrownBy evalOld(code) should have message "Object Number does not define method foo."
}
"Methods defined on Any type" should "be callable for any type" in {
@ -69,7 +69,7 @@ class MethodsTest extends InterpreterTest {
| 0
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("1", "2", "3", "0", "0", "0")
}
}

View File

@ -12,7 +12,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@addTen [@Unit, b = 10]
""".stripMargin
eval(code) shouldEqual 20
evalOld(code) shouldEqual 20
}
"Functions" should "be able to have named arguments given out of order" in {
@ -23,7 +23,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@subtract [@Unit, b = 10, a = 5]
""".stripMargin
eval(code) shouldEqual -5
evalOld(code) shouldEqual -5
}
"Functions" should "be able to have scope values as named arguments" in {
@ -37,7 +37,7 @@ class NamedArgumentsTest extends InterpreterTest {
|}
""".stripMargin
eval(code) shouldEqual 20
evalOld(code) shouldEqual 20
}
"Functions" should "be able to be defined with default argument values" in {
@ -48,7 +48,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@addNum [@Unit, 5]
""".stripMargin
eval(code) shouldEqual 15
evalOld(code) shouldEqual 15
}
"Default arguments" should "be able to default to complex expressions" in {
@ -61,7 +61,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@doThing [@Unit, 10]
|""".stripMargin
eval(code) shouldEqual 13
evalOld(code) shouldEqual 13
}
"Default arguments" should "be able to close over their outer scope" in {
@ -77,7 +77,7 @@ class NamedArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 1
evalOld(code) shouldEqual 1
}
"Functions" should "use their default values when none is supplied" in {
@ -88,7 +88,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@addTogether [@Unit]
""".stripMargin
eval(code) shouldEqual 11
evalOld(code) shouldEqual 11
}
"Functions" should "override defaults by name" in {
@ -99,7 +99,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@addNum [@Unit, 1, num = 1]
""".stripMargin
eval(code) shouldEqual 2
evalOld(code) shouldEqual 2
}
"Functions" should "override defaults by position" in {
@ -110,7 +110,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@addNum [@Unit, 1, 2]
|""".stripMargin
eval(code) shouldEqual 3
evalOld(code) shouldEqual 3
}
"Defaulted arguments" should "work in a recursive context" in {
@ -127,7 +127,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@summer [@Unit, 100]
""".stripMargin
eval(code) shouldEqual 5050
evalOld(code) shouldEqual 5050
}
"Named Arguments" should "only be scoped to their definitions" in {
@ -146,7 +146,7 @@ class NamedArgumentsTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 0
evalOld(code) shouldEqual 0
}
"Named arguments" should "be applied in a sequence compatible with Eta-expansions" in {
@ -166,7 +166,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@doubleOrAdd [@Unit, 5]
|""".stripMargin
eval(code) shouldEqual 10
evalOld(code) shouldEqual 10
}
"Default arguments" should "not be able to depend on later arguments" in {
@ -178,7 +178,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@badArgFn [@Unit, 3]
|""".stripMargin
an[InterpreterException] should be thrownBy eval(code)
an[InterpreterException] should be thrownBy evalOld(code)
}
"Constructors" should "be able to use named arguments" in {
@ -200,7 +200,7 @@ class NamedArgumentsTest extends InterpreterTest {
|}
""".stripMargin
eval(code) shouldEqual 55
evalOld(code) shouldEqual 55
}
"Constructors" should "be able to take default arguments that are overridden" in {
@ -221,7 +221,7 @@ class NamedArgumentsTest extends InterpreterTest {
|}
""".stripMargin
eval(code) shouldEqual 15
evalOld(code) shouldEqual 15
}
"Default arguments to constructors" should "be resolved dynamically" in {
@ -233,7 +233,7 @@ class NamedArgumentsTest extends InterpreterTest {
|5
|""".stripMargin
eval(code) shouldEqual 5
evalOld(code) shouldEqual 5
}
"Constructors" should "be able to take and use default arguments" in {
@ -250,7 +250,7 @@ class NamedArgumentsTest extends InterpreterTest {
|@sumList [@Unit, @Cons2 [10]]
""".stripMargin
eval(code) shouldEqual 10
evalOld(code) shouldEqual 10
}
}

View File

@ -3,10 +3,23 @@ package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterTest
class SimpleArithmeticTest extends InterpreterTest {
"1" should "equal 1" in {
eval("1") shouldEqual 1
}
"1 + 1" should "equal 2" in {
eval("1 + 1") shouldEqual 2
}
"2 + (2 * 2)" should "equal 6" in {
eval("2 + (2 * 2)") shouldEqual 6
}
"2 + 2 * 3" should "equal 8" in {
eval("2 + 2 * 3") shouldEqual 8
}
"2 * 2 / 2" should "equal 2" in {
eval("2 * 2 / 2") shouldEqual 2
}
}

View File

@ -14,7 +14,7 @@ class StateTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 11
evalOld(code) shouldEqual 11
}
"State" should "be implicitly threaded through function executions" in {
@ -36,7 +36,7 @@ class StateTest extends InterpreterTest {
|}
|""".stripMargin
eval(code) shouldEqual 5
evalOld(code) shouldEqual 5
}
"State" should "be localized with State.run" in {
@ -54,7 +54,7 @@ class StateTest extends InterpreterTest {
| res + state
|}
|""".stripMargin
eval(code) shouldEqual 30
evalOld(code) shouldEqual 30
}
"State" should "work well with recursive code" in {
@ -70,7 +70,7 @@ class StateTest extends InterpreterTest {
| @run [@State, 0, @stateSum [10]]
|}
|""".stripMargin
eval(code) shouldEqual 55
evalOld(code) shouldEqual 55
}
"State" should "be initialized to a Unit by default" in {
@ -78,7 +78,7 @@ class StateTest extends InterpreterTest {
"""
|@println[@IO, @get[@State]]
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("Unit<>")
}
@ -98,7 +98,7 @@ class StateTest extends InterpreterTest {
| 0
|}
|""".stripMargin
eval(code)
evalOld(code)
consumeOut shouldEqual List("11", "16")
}
@ -112,6 +112,6 @@ class StateTest extends InterpreterTest {
| @get[@State]
|}
|""".stripMargin
eval(code) shouldEqual (-5)
evalOld(code) shouldEqual (-5)
}
}

View File

@ -9,7 +9,7 @@ class StringTest extends InterpreterTest {
|@println [@IO, "hello world!"]
|""".stripMargin
noException shouldBe thrownBy(eval(code))
noException shouldBe thrownBy(evalOld(code))
consumeOut shouldEqual List("hello world!")
}
}