From 4d6426c899aaa7da55ec678d02b68473ac8120df Mon Sep 17 00:00:00 2001 From: os222 Date: Sat, 24 May 2025 22:13:16 +0100 Subject: [PATCH] Initial commit - copied from GitLab --- .gitignore | 4 + .gitlab-ci.yml | 16 + Makefile | 21 + README.md | 35 + compile | 9 + project.scala | 26 + src/main/wacc/Main.scala | 109 ++ src/main/wacc/backend/codeGen.scala | 754 +++++++++++ src/main/wacc/backend/expressionGen.scala | 449 +++++++ src/main/wacc/backend/instructions.scala | 471 +++++++ src/main/wacc/backend/labelGen.scala | 399 ++++++ src/main/wacc/backend/lhsRhsGen.scala | 311 +++++ src/main/wacc/backend/registerAllocator.scala | 288 +++++ src/main/wacc/backend/typeAnalysis.scala | 75 ++ src/main/wacc/extension/peephole.scala | 267 ++++ .../wacc/frontend/semantic/environment.scala | 185 +++ src/main/wacc/frontend/semantic/renamer.scala | 274 ++++ .../frontend/semantic/semanticAnalyser.scala | 18 + .../frontend/semantic/semanticErrors.scala | 156 +++ src/main/wacc/frontend/semantic/symbols.scala | 20 + .../wacc/frontend/semantic/typeChecking.scala | 293 +++++ src/main/wacc/frontend/syntax/ast.scala | 466 +++++++ src/main/wacc/frontend/syntax/imports.scala | 40 + src/main/wacc/frontend/syntax/lexer.scala | 69 ++ src/main/wacc/frontend/syntax/parser.scala | 259 ++++ .../wacc/frontend/syntax/syntaxErrors.scala | 17 + src/test/wacc/codeGen.scala | 114 ++ src/test/wacc/codeGenTestConfig.scala | 194 +++ src/test/wacc/imports.scala | 121 ++ src/test/wacc/importsParallel.scala | 23 + src/test/wacc/parser.scala | 171 +++ src/test/wacc/peephole.scala | 119 ++ src/test/wacc/semanticAnalyser.scala | 143 +++ src/test/wacc/testConfig.scala | 131 ++ .../semanticErr/IO/readTypeErr.wacc | 15 + .../array/arrayIndexComplexNotInt.wacc | 16 + .../semanticErr/array/arrayIndexNotInt.wacc | 15 + .../array/arrayMultipleIndexError.wacc | 20 + .../semanticErr/array/badIndex.wacc | 15 + .../semanticErr/array/indexUndefIdent.wacc | 14 + .../array/mixingTypesInArrays.wacc | 19 + .../semanticErr/array/noArrayCovariance.wacc | 15 + .../semanticErr/array/noStringIndex.wacc | 14 + .../semanticErr/array/nonMatchingArrays.wacc | 16 + .../array/wrongArrayDimension.wacc | 15 + .../semanticErr/array/wrongArrayType.wacc | 14 + .../semanticErr/exit/badCharExit.wacc | 13 + .../semanticErr/exit/exitNonInt.wacc | 14 + .../semanticErr/exit/globalReturn.wacc | 14 + .../semanticErr/exit/returnsInMain.wacc | 26 + .../expressions/boolOpTypeErr.wacc | 13 + .../semanticErr/expressions/exprTypeErr.wacc | 13 + .../semanticErr/expressions/intOpTypeErr.wacc | 13 + .../semanticErr/expressions/lessPairExpr.wacc | 17 + .../expressions/mixedOpTypeErr.wacc | 13 + .../semanticErr/expressions/moreArrExpr.wacc | 15 + .../expressions/stringElemErr.wacc | 16 + .../semanticErr/function/ambiguousCall.wacc | 23 + .../semanticErr/function/ambiguousCall2.wacc | 25 + .../function/ambiguousOverload.wacc | 27 + .../function/callHasNoMatchingOverload.wacc | 24 + .../function/callUndefFunction.wacc | 14 + .../semanticErr/function/doubleArgDef.wacc | 17 + .../semanticErr/function/funcVarAccess.wacc | 20 + .../semanticErr/function/functionAssign.wacc | 17 + .../function/functionBadArgUse.wacc | 18 + .../semanticErr/function/functionBadCall.wacc | 18 + .../function/functionBadCall2.wacc | 23 + .../function/functionBadParam.wacc | 18 + .../function/functionBadParam2.wacc | 24 + .../function/functionBadReturn.wacc | 18 + .../function/functionBadReturn2.wacc | 23 + .../function/functionBadReturn3.wacc | 23 + .../function/functionOverArgs.wacc | 18 + .../function/functionOverArgs2.wacc | 23 + .../function/functionRedefine.wacc | 20 + .../function/functionRedefine2.wacc | 20 + .../function/functionSwapArgs.wacc | 19 + .../function/functionUnderArgs.wacc | 19 + .../function/invalidReturnsBranched.wacc | 22 + .../function/mismatchingReturns.wacc | 27 + .../semanticErr/if/ifIntCondition.wacc | 17 + .../semanticErr/multiple/funcMess.wacc | 25 + .../semanticErr/multiple/ifAndWhileErrs.wacc | 19 + .../semanticErr/multiple/messyExpr.wacc | 16 + .../multiple/multiCaseSensitivity.wacc | 20 + .../semanticErr/multiple/multiTypeErrs.wacc | 15 + .../multiple/obfuscatingReturnsWithWhile.wacc | 44 + .../semanticErr/pairs/badPairAssign.wacc | 13 + .../semanticErr/pairs/badPairExchange.wacc | 15 + .../semanticErr/pairs/freeNonPair.wacc | 16 + .../semanticErr/pairs/mismatchedPair.wacc | 13 + .../semanticErr/pairs/noPairCovariance.wacc | 15 + .../semanticErr/pairs/nonMatchingPairs.wacc | 16 + .../semanticErr/pairs/readUnknown.wacc | 14 + .../pairs/wrongTypeInParameterlessPair.wacc | 18 + .../semanticErr/print/printTypeErr01.wacc | 15 + .../semanticErr/read/readIntoBadFst.wacc | 14 + .../semanticErr/read/readIntoBadSnd.wacc | 14 + .../semanticErr/read/readTypeErr01.wacc | 15 + .../semanticErr/scope/badParentScope.wacc | 20 + .../semanticErr/scope/badScopeRedefine.wacc | 19 + .../semanticErr/variables/basicTypeErr01.wacc | 13 + .../semanticErr/variables/basicTypeErr02.wacc | 13 + .../semanticErr/variables/basicTypeErr03.wacc | 13 + .../semanticErr/variables/basicTypeErr04.wacc | 13 + .../semanticErr/variables/basicTypeErr05.wacc | 13 + .../semanticErr/variables/basicTypeErr06.wacc | 13 + .../semanticErr/variables/basicTypeErr07.wacc | 13 + .../semanticErr/variables/basicTypeErr08.wacc | 13 + .../semanticErr/variables/basicTypeErr09.wacc | 13 + .../semanticErr/variables/basicTypeErr10.wacc | 13 + .../semanticErr/variables/basicTypeErr11.wacc | 13 + .../semanticErr/variables/basicTypeErr12.wacc | 13 + .../semanticErr/variables/caseMatters.wacc | 16 + .../semanticErr/variables/doubleDeclare.wacc | 14 + .../variables/undeclaredScopeVar.wacc | 16 + .../semanticErr/variables/undeclaredVar.wacc | 13 + .../variables/undeclaredVarAccess.wacc | 14 + .../semanticErr/while/falsErr.wacc | 15 + .../semanticErr/while/truErr.wacc | 15 + .../semanticErr/while/whileIntCondition.wacc | 15 + .../syntaxErr/array/arrayExpr.wacc | 14 + .../syntaxErr/basic/badComment.wacc | 14 + .../syntaxErr/basic/badComment2.wacc | 15 + .../syntaxErr/basic/badEscape.wacc | 13 + .../syntaxErr/basic/beginNoend.wacc | 11 + .../waccPrograms/syntaxErr/basic/bgnErr.wacc | 11 + .../syntaxErr/basic/multipleBegins.wacc | 17 + .../waccPrograms/syntaxErr/basic/noBody.wacc | 11 + .../waccPrograms/syntaxErr/basic/skpErr.wacc | 11 + .../syntaxErr/basic/unescapedChar.wacc | 13 + .../expressions/missingOperand1.wacc | 13 + .../expressions/missingOperand2.wacc | 13 + .../syntaxErr/expressions/printlnConcat.wacc | 13 + .../syntaxErr/function/badlyNamed.wacc | 17 + .../syntaxErr/function/badlyPlaced.wacc | 17 + .../syntaxErr/function/funcExpr.wacc | 17 + .../syntaxErr/function/funcExpr2.wacc | 23 + .../function/functionConditionalNoReturn.wacc | 27 + .../function/functionEndingNotReturn.wacc | 19 + .../function/functionLateDefine.wacc | 23 + .../function/functionMissingCall.wacc | 17 + .../function/functionMissingPType.wacc | 16 + .../function/functionMissingParam.wacc | 17 + .../function/functionMissingType.wacc | 17 + .../syntaxErr/function/functionNoReturn.wacc | 18 + .../function/functionReturnInLoop.wacc | 26 + .../syntaxErr/function/functionScopeDef.wacc | 21 + .../function/mutualRecursionNoReturn.wacc | 31 + .../syntaxErr/function/noBodyAfterFuncs.wacc | 16 + .../syntaxErr/function/thisIsNotC.wacc | 22 + .../waccPrograms/syntaxErr/if/ifNoelse.wacc | 16 + .../waccPrograms/syntaxErr/if/ifNofi.wacc | 17 + .../waccPrograms/syntaxErr/if/ifNothen.wacc | 17 + .../waccPrograms/syntaxErr/if/ifiErr.wacc | 18 + .../syntaxErr/literals/charLiteralSingle.wacc | 14 + .../literals/stringLiteralNoNewlines.wacc | 14 + .../literals/stringLiteralOnlyAscii.wacc | 13 + .../syntaxErr/pairs/badLookup01.wacc | 14 + .../syntaxErr/pairs/badLookup02.wacc | 14 + .../syntaxErr/pairs/elemOfNonPair.wacc | 16 + .../waccPrograms/syntaxErr/pairs/fstNull.wacc | 14 + .../syntaxErr/pairs/noNesting.wacc | 13 + .../waccPrograms/syntaxErr/pairs/sndNull.wacc | 14 + .../syntaxErr/print/printlnCharArry.wacc | 13 + .../syntaxErr/sequence/doubleSeq.wacc | 14 + .../syntaxErr/sequence/emptySeq.wacc | 11 + .../syntaxErr/sequence/endSeq.wacc | 11 + .../syntaxErr/sequence/extraSeq.wacc | 11 + .../syntaxErr/sequence/missingSeq.wacc | 14 + .../variables/badintAssignments.wacc | 14 + .../variables/badintAssignments1.wacc | 13 + .../variables/badintAssignments2.wacc | 13 + .../syntaxErr/variables/bigIntAssignment.wacc | 13 + .../syntaxErr/variables/varNoName.wacc | 14 + .../waccPrograms/syntaxErr/while/donoErr.wacc | 15 + .../waccPrograms/syntaxErr/while/dooErr.wacc | 15 + .../waccPrograms/syntaxErr/while/whilErr.wacc | 15 + .../syntaxErr/while/whileNodo.wacc | 15 + .../syntaxErr/while/whileNodone.wacc | 14 + .../valid/IO/print/hashInProgram.wacc | 15 + .../IO/print/multipleStringsAssignment.wacc | 40 + .../valid/IO/print/print-backspace.wacc | 11 + .../waccPrograms/valid/IO/print/print.wacc | 10 + .../valid/IO/print/printBool.wacc | 16 + .../valid/IO/print/printChar.wacc | 12 + .../valid/IO/print/printCharArray.wacc | 15 + .../valid/IO/print/printCharAsString.wacc | 15 + .../valid/IO/print/printEscChar.wacc | 12 + .../waccPrograms/valid/IO/print/printInt.wacc | 12 + .../waccPrograms/valid/IO/print/println.wacc | 11 + .../valid/IO/read/echoBigInt.wacc | 17 + .../valid/IO/read/echoBigNegInt.wacc | 17 + .../waccPrograms/valid/IO/read/echoChar.wacc | 17 + .../waccPrograms/valid/IO/read/echoInt.wacc | 17 + .../valid/IO/read/echoNegInt.wacc | 17 + .../valid/IO/read/echoPuncChar.wacc | 17 + .../wacc/waccPrograms/valid/IO/read/read.wacc | 15 + .../waccPrograms/valid/IO/read/readAtEof.wacc | 19 + .../waccPrograms/valid/IO/special/IOLoop.wacc | 40 + .../valid/IO/special/IOSequence.wacc | 17 + .../valid/advanced/binarySortTree.wacc | 83 ++ .../valid/advanced/hashTable.wacc | 321 +++++ .../valid/advanced/ticTacToe.wacc | 1097 +++++++++++++++++ .../wacc/waccPrograms/valid/array/array.wacc | 32 + .../waccPrograms/valid/array/arrayBasic.wacc | 9 + .../waccPrograms/valid/array/arrayEmpty.wacc | 9 + .../array/arrayIndexMayBeArrayIndex.wacc | 24 + .../waccPrograms/valid/array/arrayLength.wacc | 12 + .../waccPrograms/valid/array/arrayLookup.wacc | 12 + .../waccPrograms/valid/array/arrayNested.wacc | 16 + .../waccPrograms/valid/array/arrayOnHeap.wacc | 26 + .../waccPrograms/valid/array/arrayPrint.wacc | 27 + .../waccPrograms/valid/array/arraySimple.wacc | 13 + .../valid/array/charArrayInStringArray.wacc | 11 + .../valid/array/emptyArrayAloneIsFine.wacc | 9 + .../valid/array/emptyArrayNextLine.wacc | 10 + .../valid/array/emptyArrayPrint.wacc | 12 + .../valid/array/emptyArrayReplace.wacc | 13 + .../valid/array/emptyArrayScope.wacc | 12 + .../waccPrograms/valid/array/freeArray.wacc | 12 + .../valid/array/lenArrayIndex.wacc | 13 + .../valid/array/modifyString.wacc | 18 + .../waccPrograms/valid/array/printRef.wacc | 13 + .../valid/array/readIntoArray.wacc | 17 + .../valid/array/stringFromArray.wacc | 10 + .../waccPrograms/valid/basic/exit/exit-1.wacc | 12 + .../valid/basic/exit/exitBasic.wacc | 12 + .../valid/basic/exit/exitBasic2.wacc | 12 + .../valid/basic/exit/exitWrap.wacc | 12 + .../valid/basic/skip/comment.wacc | 10 + .../valid/basic/skip/commentEoF.wacc | 10 + .../valid/basic/skip/commentInLine.wacc | 9 + .../waccPrograms/valid/basic/skip/skip.wacc | 7 + .../valid/expressions/andExpr.wacc | 17 + .../valid/expressions/andOverOrExpr.wacc | 16 + .../valid/expressions/boolCalc.wacc | 14 + .../valid/expressions/boolExpr1.wacc | 16 + .../valid/expressions/charComparisonExpr.wacc | 23 + .../valid/expressions/divExpr.wacc | 13 + .../valid/expressions/equalsExpr.wacc | 19 + .../valid/expressions/equalsOverAnd.wacc | 16 + .../valid/expressions/equalsOverBool.wacc | 17 + .../valid/expressions/equalsOverOr.wacc | 16 + .../valid/expressions/greaterEqExpr.wacc | 19 + .../valid/expressions/greaterExpr.wacc | 16 + .../valid/expressions/ifExpr1.wacc | 11 + .../valid/expressions/ifExpr2.wacc | 13 + .../valid/expressions/ifExpr3.wacc | 15 + .../valid/expressions/ifExpr4.wacc | 16 + .../valid/expressions/ifExpr5.wacc | 13 + .../valid/expressions/ifExpr6.wacc | 13 + .../valid/expressions/ifExpr7.wacc | 13 + .../valid/expressions/intCalc.wacc | 14 + .../valid/expressions/intExpr1.wacc | 16 + .../valid/expressions/lessCharExpr.wacc | 16 + .../valid/expressions/lessEqExpr.wacc | 19 + .../valid/expressions/lessExpr.wacc | 16 + .../valid/expressions/longExpr.wacc | 13 + .../valid/expressions/longExpr2.wacc | 13 + .../valid/expressions/longExpr3.wacc | 13 + .../valid/expressions/longSplitExpr.wacc | 21 + .../valid/expressions/longSplitExpr2.wacc | 21 + .../valid/expressions/minusExpr.wacc | 13 + .../valid/expressions/minusMinusExpr.wacc | 11 + .../expressions/minusNoWhitespaceExpr.wacc | 11 + .../valid/expressions/minusPlusExpr.wacc | 11 + .../valid/expressions/modExpr.wacc | 13 + .../valid/expressions/multExpr.wacc | 13 + .../expressions/multNoWhitespaceExpr.wacc | 11 + .../valid/expressions/negBothDiv.wacc | 13 + .../valid/expressions/negBothMod.wacc | 13 + .../valid/expressions/negDividendDiv.wacc | 13 + .../valid/expressions/negDividendMod.wacc | 13 + .../valid/expressions/negDivisorDiv.wacc | 13 + .../valid/expressions/negDivisorMod.wacc | 13 + .../valid/expressions/negExpr.wacc | 12 + .../valid/expressions/notExpr.wacc | 15 + .../valid/expressions/notequalsExpr.wacc | 19 + .../valid/expressions/orExpr.wacc | 17 + .../valid/expressions/ordAndchrExpr.wacc | 21 + .../valid/expressions/plusExpr.wacc | 13 + .../valid/expressions/plusMinusExpr.wacc | 11 + .../expressions/plusNoWhitespaceExpr.wacc | 11 + .../valid/expressions/plusPlusExpr.wacc | 11 + .../valid/expressions/sequentialCount.wacc | 41 + .../valid/expressions/stringEqualsExpr.wacc | 19 + .../imported_functions/asciiTableImport.wacc | 117 ++ .../imported_functions/basicImport.wacc | 16 + .../imported_functions/manyImports.wacc | 20 + .../imported_functions/multipleImports.wacc | 19 + .../imported_functions/nestedImport.wacc | 15 + .../imported_functions/overloadedImport.wacc | 18 + .../imported_functions/recursiveImport.wacc | 29 + .../recursiveManyImports.wacc | 34 + .../nested_functions/fibonacciFullRec.wacc | 35 + .../nested_functions/fibonacciRecursive.wacc | 35 + .../fixedPointRealArithmetic.wacc | 123 ++ .../functionConditionalReturn.wacc | 19 + .../nested_functions/mutualRecursion.wacc | 46 + .../nested_functions/printInputTriangle.wacc | 45 + .../nested_functions/printTriangle.wacc | 33 + .../nested_functions/simpleRecursion.wacc | 20 + .../overload_functions/complexOverload.wacc | 116 ++ .../differentParameterCounts.wacc | 24 + .../functionCharIntoString.wacc | 24 + .../functionCharIntoString2.wacc | 24 + .../overloadingWithArrays.wacc | 26 + .../overloadingWithPairs.wacc | 30 + .../overload_functions/uniqueParameters.wacc | 24 + .../overload_functions/uniqueSignatures.wacc | 24 + .../argScopeCanBeShadowed.wacc | 18 + .../function/simple_functions/asciiTable.wacc | 139 +++ .../function/simple_functions/clashNames.wacc | 21 + .../functionArrayDoesntOverwrite.wacc | 18 + .../simple_functions/functionDeclaration.wacc | 12 + .../functionDoubleReturn.wacc | 16 + .../simple_functions/functionIfReturns.wacc | 23 + .../functionManyArguments.wacc | 36 + .../functionMultiReturns.wacc | 20 + .../functionOverArguments.wacc | 22 + .../functionOverArguments2.wacc | 63 + .../simple_functions/functionReturnPair.wacc | 19 + .../simple_functions/functionSimple.wacc | 15 + .../simple_functions/functionSimpleLoop.wacc | 19 + .../functionUpdateParameter.wacc | 30 + .../simple_functions/incFunction.wacc | 21 + .../simple_functions/lotsOfLocals.wacc | 32 + .../simple_functions/manyArgumentsChar.wacc | 27 + .../simple_functions/manyArgumentsInt.wacc | 21 + .../simple_functions/negFunction.wacc | 23 + .../function/simple_functions/punning.wacc | 15 + .../simple_functions/sameArgName.wacc | 16 + .../simple_functions/sameArgName2.wacc | 17 + .../simple_functions/sameNameAsVar.wacc | 16 + .../usesArgumentWhilstMakingArgument.wacc | 25 + src/test/wacc/waccPrograms/valid/if/if1.wacc | 17 + src/test/wacc/waccPrograms/valid/if/if2.wacc | 17 + src/test/wacc/waccPrograms/valid/if/if3.wacc | 18 + src/test/wacc/waccPrograms/valid/if/if4.wacc | 18 + src/test/wacc/waccPrograms/valid/if/if5.wacc | 18 + src/test/wacc/waccPrograms/valid/if/if6.wacc | 18 + .../wacc/waccPrograms/valid/if/ifBasic.wacc | 15 + .../wacc/waccPrograms/valid/if/ifFalse.wacc | 16 + .../wacc/waccPrograms/valid/if/ifTrue.wacc | 16 + .../waccPrograms/valid/if/whitespace.wacc | 13 + .../valid/pairs/checkRefPair.wacc | 33 + .../waccPrograms/valid/pairs/createPair.wacc | 9 + .../valid/pairs/createPair02.wacc | 9 + .../valid/pairs/createPair03.wacc | 9 + .../valid/pairs/createRefPair.wacc | 10 + .../waccPrograms/valid/pairs/freePairs.wacc | 11 + .../waccPrograms/valid/pairs/linkedList.wacc | 28 + .../waccPrograms/valid/pairs/nestedPair.wacc | 10 + .../valid/pairs/nestedPairLeftAssign.wacc | 15 + .../valid/pairs/nestedPairRightExtract.wacc | 14 + .../wacc/waccPrograms/valid/pairs/null.wacc | 15 + .../valid/pairs/pairExchangeArrayOk.wacc | 12 + .../waccPrograms/valid/pairs/pairarray.wacc | 15 + .../waccPrograms/valid/pairs/printNull.wacc | 11 + .../valid/pairs/printNullPair.wacc | 12 + .../waccPrograms/valid/pairs/printPair.wacc | 19 + .../valid/pairs/printPairOfNulls.wacc | 19 + .../valid/pairs/readIntoPair.wacc | 21 + .../waccPrograms/valid/pairs/readPair.wacc | 31 + .../waccPrograms/valid/pairs/writeFst.wacc | 17 + .../waccPrograms/valid/pairs/writeSnd.wacc | 17 + .../arrayOutOfBounds/arrayNegBounds.wacc | 15 + .../arrayOutOfBounds/arrayOutOfBounds.wacc | 16 + .../arrayOutOfBoundsWrite.wacc | 17 + .../valid/runtimeErr/badChar/negativeChr.wacc | 13 + .../valid/runtimeErr/badChar/tooBigChr.wacc | 13 + .../runtimeErr/divideByZero/divZero.wacc | 14 + .../runtimeErr/divideByZero/divideByZero.wacc | 15 + .../runtimeErr/divideByZero/modByZero.wacc | 15 + .../integerOverflow/intJustOverflow.wacc | 20 + .../integerOverflow/intUnderflow.wacc | 20 + .../integerOverflow/intWayOverflow.wacc | 17 + .../integerOverflow/intmultOverflow.wacc | 22 + .../integerOverflow/intnegateOverflow.wacc | 17 + .../integerOverflow/intnegateOverflow2.wacc | 17 + .../integerOverflow/intnegateOverflow3.wacc | 17 + .../integerOverflow/intnegateOverflow4.wacc | 17 + .../runtimeErr/nullDereference/freeNull.wacc | 15 + .../runtimeErr/nullDereference/readNull1.wacc | 14 + .../runtimeErr/nullDereference/readNull2.wacc | 14 + .../runtimeErr/nullDereference/setNull1.wacc | 14 + .../runtimeErr/nullDereference/setNull2.wacc | 14 + .../runtimeErr/nullDereference/useNull1.wacc | 14 + .../runtimeErr/nullDereference/useNull2.wacc | 14 + .../waccPrograms/valid/scope/ifNested1.wacc | 22 + .../waccPrograms/valid/scope/ifNested2.wacc | 37 + .../valid/scope/indentationNotImportant.wacc | 13 + .../valid/scope/intsAndKeywords.wacc | 9 + .../valid/scope/printAllTypes.wacc | 126 ++ .../wacc/waccPrograms/valid/scope/scope.wacc | 18 + .../waccPrograms/valid/scope/scopeBasic.wacc | 13 + .../valid/scope/scopeIfRedefine.wacc | 20 + .../valid/scope/scopeRedefine.wacc | 18 + .../valid/scope/scopeSimpleRedefine.wacc | 17 + .../waccPrograms/valid/scope/scopeVars.wacc | 19 + .../valid/scope/scopeWhileNested.wacc | 26 + .../valid/scope/scopeWhileRedefine.wacc | 25 + .../waccPrograms/valid/scope/splitScope.wacc | 19 + .../waccPrograms/valid/sequence/basicSeq.wacc | 10 + .../valid/sequence/basicSeq2.wacc | 11 + .../valid/sequence/boolAssignment.wacc | 10 + .../valid/sequence/charAssignment.wacc | 10 + .../valid/sequence/exitSimple.wacc | 13 + .../valid/sequence/intAssignment.wacc | 14 + .../valid/sequence/intLeadingZeros.wacc | 15 + .../valid/sequence/stringAssignment.wacc | 10 + .../valid/variables/_VarNames.wacc | 13 + .../valid/variables/boolDeclaration.wacc | 9 + .../valid/variables/boolDeclaration2.wacc | 9 + .../valid/variables/capCharDeclaration.wacc | 9 + .../valid/variables/charDeclaration.wacc | 9 + .../valid/variables/charDeclaration2.wacc | 9 + .../variables/emptyStringDeclaration.wacc | 9 + .../valid/variables/intDeclaration.wacc | 9 + .../valid/variables/longVarNames.wacc | 13 + .../valid/variables/manyVariables.wacc | 265 ++++ .../valid/variables/negIntDeclaration.wacc | 9 + .../valid/variables/puncCharDeclaration.wacc | 9 + .../valid/variables/stringCarriageReturn.wacc | 9 + .../valid/variables/stringDeclaration.wacc | 9 + .../valid/variables/zeroIntDeclaration.wacc | 9 + .../valid/while/fibonacciFullIt.wacc | 31 + .../valid/while/fibonacciIterative.wacc | 25 + .../valid/while/loopCharCondition.wacc | 17 + .../valid/while/loopIntCondition.wacc | 17 + .../wacc/waccPrograms/valid/while/max.wacc | 20 + .../wacc/waccPrograms/valid/while/min.wacc | 20 + .../waccPrograms/valid/while/rmStyleAdd.wacc | 24 + .../valid/while/rmStyleAddIO.wacc | 30 + .../waccPrograms/valid/while/whileBasic.wacc | 11 + .../valid/while/whileBoolFlip.wacc | 17 + .../waccPrograms/valid/while/whileCount.wacc | 26 + .../waccPrograms/valid/while/whileFalse.wacc | 14 + 440 files changed, 15150 insertions(+) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 Makefile create mode 100644 README.md create mode 100755 compile create mode 100644 project.scala create mode 100644 src/main/wacc/Main.scala create mode 100644 src/main/wacc/backend/codeGen.scala create mode 100644 src/main/wacc/backend/expressionGen.scala create mode 100644 src/main/wacc/backend/instructions.scala create mode 100644 src/main/wacc/backend/labelGen.scala create mode 100644 src/main/wacc/backend/lhsRhsGen.scala create mode 100644 src/main/wacc/backend/registerAllocator.scala create mode 100644 src/main/wacc/backend/typeAnalysis.scala create mode 100644 src/main/wacc/extension/peephole.scala create mode 100644 src/main/wacc/frontend/semantic/environment.scala create mode 100644 src/main/wacc/frontend/semantic/renamer.scala create mode 100644 src/main/wacc/frontend/semantic/semanticAnalyser.scala create mode 100644 src/main/wacc/frontend/semantic/semanticErrors.scala create mode 100644 src/main/wacc/frontend/semantic/symbols.scala create mode 100644 src/main/wacc/frontend/semantic/typeChecking.scala create mode 100644 src/main/wacc/frontend/syntax/ast.scala create mode 100644 src/main/wacc/frontend/syntax/imports.scala create mode 100644 src/main/wacc/frontend/syntax/lexer.scala create mode 100644 src/main/wacc/frontend/syntax/parser.scala create mode 100644 src/main/wacc/frontend/syntax/syntaxErrors.scala create mode 100644 src/test/wacc/codeGen.scala create mode 100644 src/test/wacc/codeGenTestConfig.scala create mode 100644 src/test/wacc/imports.scala create mode 100644 src/test/wacc/importsParallel.scala create mode 100644 src/test/wacc/parser.scala create mode 100644 src/test/wacc/peephole.scala create mode 100644 src/test/wacc/semanticAnalyser.scala create mode 100644 src/test/wacc/testConfig.scala create mode 100644 src/test/wacc/waccPrograms/semanticErr/IO/readTypeErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/arrayIndexComplexNotInt.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/arrayIndexNotInt.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/arrayMultipleIndexError.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/badIndex.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/indexUndefIdent.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/mixingTypesInArrays.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/noArrayCovariance.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/noStringIndex.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/nonMatchingArrays.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/wrongArrayDimension.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/array/wrongArrayType.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/exit/badCharExit.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/exit/exitNonInt.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/exit/globalReturn.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/exit/returnsInMain.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/expressions/boolOpTypeErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/expressions/exprTypeErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/expressions/intOpTypeErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/expressions/lessPairExpr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/expressions/mixedOpTypeErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/expressions/moreArrExpr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/expressions/stringElemErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall2.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/ambiguousOverload.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/callHasNoMatchingOverload.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/callUndefFunction.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/doubleArgDef.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/funcVarAccess.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionAssign.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadArgUse.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadCall.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadCall2.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadParam.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadParam2.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn2.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn3.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs2.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionRedefine.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionRedefine2.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionSwapArgs.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/functionUnderArgs.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/invalidReturnsBranched.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/function/mismatchingReturns.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/if/ifIntCondition.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/multiple/funcMess.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/multiple/ifAndWhileErrs.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/multiple/messyExpr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/multiple/multiCaseSensitivity.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/multiple/multiTypeErrs.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/multiple/obfuscatingReturnsWithWhile.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/badPairAssign.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/badPairExchange.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/freeNonPair.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/mismatchedPair.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/noPairCovariance.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/nonMatchingPairs.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/readUnknown.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/pairs/wrongTypeInParameterlessPair.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/print/printTypeErr01.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/read/readIntoBadFst.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/read/readIntoBadSnd.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/read/readTypeErr01.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/scope/badParentScope.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/scope/badScopeRedefine.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr01.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr02.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr03.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr04.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr05.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr06.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr07.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr08.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr09.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr10.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr11.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr12.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/caseMatters.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/doubleDeclare.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/undeclaredScopeVar.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVar.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVarAccess.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/while/falsErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/while/truErr.wacc create mode 100644 src/test/wacc/waccPrograms/semanticErr/while/whileIntCondition.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/array/arrayExpr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/badComment.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/badComment2.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/badEscape.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/beginNoend.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/bgnErr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/multipleBegins.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/noBody.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/skpErr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/basic/unescapedChar.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand1.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand2.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/expressions/printlnConcat.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/badlyNamed.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/badlyPlaced.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/funcExpr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/funcExpr2.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionConditionalNoReturn.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionEndingNotReturn.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionLateDefine.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionMissingCall.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionMissingPType.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionMissingParam.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionMissingType.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionNoReturn.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionReturnInLoop.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/functionScopeDef.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/mutualRecursionNoReturn.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/noBodyAfterFuncs.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/function/thisIsNotC.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/if/ifNoelse.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/if/ifNofi.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/if/ifNothen.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/if/ifiErr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/literals/charLiteralSingle.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralNoNewlines.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralOnlyAscii.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup01.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup02.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/pairs/elemOfNonPair.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/pairs/fstNull.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/pairs/noNesting.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/pairs/sndNull.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/print/printlnCharArry.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/sequence/doubleSeq.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/sequence/emptySeq.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/sequence/endSeq.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/sequence/extraSeq.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/sequence/missingSeq.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments1.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments2.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/variables/bigIntAssignment.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/variables/varNoName.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/while/donoErr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/while/dooErr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/while/whilErr.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/while/whileNodo.wacc create mode 100644 src/test/wacc/waccPrograms/syntaxErr/while/whileNodone.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/hashInProgram.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/multipleStringsAssignment.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/print-backspace.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/print.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/printBool.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/printChar.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/printCharArray.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/printCharAsString.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/printEscChar.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/printInt.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/print/println.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/echoBigInt.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/echoBigNegInt.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/echoChar.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/echoInt.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/echoNegInt.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/echoPuncChar.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/read.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/read/readAtEof.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/special/IOLoop.wacc create mode 100644 src/test/wacc/waccPrograms/valid/IO/special/IOSequence.wacc create mode 100644 src/test/wacc/waccPrograms/valid/advanced/binarySortTree.wacc create mode 100644 src/test/wacc/waccPrograms/valid/advanced/hashTable.wacc create mode 100644 src/test/wacc/waccPrograms/valid/advanced/ticTacToe.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/array.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayBasic.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayEmpty.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayIndexMayBeArrayIndex.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayLength.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayLookup.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayNested.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayOnHeap.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arrayPrint.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/arraySimple.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/charArrayInStringArray.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/emptyArrayAloneIsFine.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/emptyArrayNextLine.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/emptyArrayPrint.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/emptyArrayReplace.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/emptyArrayScope.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/freeArray.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/lenArrayIndex.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/modifyString.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/printRef.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/readIntoArray.wacc create mode 100644 src/test/wacc/waccPrograms/valid/array/stringFromArray.wacc create mode 100644 src/test/wacc/waccPrograms/valid/basic/exit/exit-1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/basic/exit/exitBasic.wacc create mode 100644 src/test/wacc/waccPrograms/valid/basic/exit/exitBasic2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/basic/exit/exitWrap.wacc create mode 100644 src/test/wacc/waccPrograms/valid/basic/skip/comment.wacc create mode 100755 src/test/wacc/waccPrograms/valid/basic/skip/commentEoF.wacc create mode 100644 src/test/wacc/waccPrograms/valid/basic/skip/commentInLine.wacc create mode 100644 src/test/wacc/waccPrograms/valid/basic/skip/skip.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/andExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/andOverOrExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/boolCalc.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/boolExpr1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/charComparisonExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/divExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/equalsExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/equalsOverAnd.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/equalsOverBool.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/equalsOverOr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/greaterEqExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/greaterExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ifExpr1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ifExpr2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ifExpr3.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ifExpr4.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ifExpr5.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ifExpr6.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ifExpr7.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/intCalc.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/intExpr1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/lessCharExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/lessEqExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/lessExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/longExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/longExpr2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/longExpr3.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/longSplitExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/longSplitExpr2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/minusExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/minusMinusExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/minusNoWhitespaceExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/minusPlusExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/modExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/multExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/multNoWhitespaceExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/negBothDiv.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/negBothMod.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/negDividendDiv.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/negDividendMod.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/negDivisorDiv.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/negDivisorMod.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/negExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/notExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/notequalsExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/orExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/ordAndchrExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/plusExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/plusMinusExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/plusNoWhitespaceExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/plusPlusExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/sequentialCount.wacc create mode 100644 src/test/wacc/waccPrograms/valid/expressions/stringEqualsExpr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/asciiTableImport.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/basicImport.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/manyImports.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/multipleImports.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/nestedImport.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/overloadedImport.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveImport.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveManyImports.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciFullRec.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciRecursive.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/fixedPointRealArithmetic.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/functionConditionalReturn.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/mutualRecursion.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/printInputTriangle.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/printTriangle.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/nested_functions/simpleRecursion.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/complexOverload.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/differentParameterCounts.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithArrays.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithPairs.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueParameters.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueSignatures.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/argScopeCanBeShadowed.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/asciiTable.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/clashNames.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionArrayDoesntOverwrite.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionDoubleReturn.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionIfReturns.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionManyArguments.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionMultiReturns.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionReturnPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimple.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimpleLoop.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/functionUpdateParameter.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/incFunction.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/lotsOfLocals.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsChar.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsInt.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/negFunction.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/punning.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/sameNameAsVar.wacc create mode 100644 src/test/wacc/waccPrograms/valid/function/simple_functions/usesArgumentWhilstMakingArgument.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/if1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/if2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/if3.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/if4.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/if5.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/if6.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/ifBasic.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/ifFalse.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/ifTrue.wacc create mode 100644 src/test/wacc/waccPrograms/valid/if/whitespace.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/checkRefPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/createPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/createPair02.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/createPair03.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/createRefPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/freePairs.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/linkedList.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/nestedPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/nestedPairLeftAssign.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/nestedPairRightExtract.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/null.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/pairExchangeArrayOk.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/pairarray.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/printNull.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/printNullPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/printPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/printPairOfNulls.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/readIntoPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/readPair.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/writeFst.wacc create mode 100644 src/test/wacc/waccPrograms/valid/pairs/writeSnd.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayNegBounds.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBounds.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBoundsWrite.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/badChar/negativeChr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/badChar/tooBigChr.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divZero.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divideByZero.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/modByZero.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intJustOverflow.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intUnderflow.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intWayOverflow.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intmultOverflow.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow3.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow4.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/freeNull.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/ifNested1.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/ifNested2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/indentationNotImportant.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/intsAndKeywords.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/printAllTypes.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scope.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scopeBasic.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scopeIfRedefine.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scopeRedefine.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scopeSimpleRedefine.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scopeVars.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scopeWhileNested.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/scopeWhileRedefine.wacc create mode 100644 src/test/wacc/waccPrograms/valid/scope/splitScope.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/basicSeq.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/basicSeq2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/boolAssignment.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/charAssignment.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/exitSimple.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/intAssignment.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/intLeadingZeros.wacc create mode 100644 src/test/wacc/waccPrograms/valid/sequence/stringAssignment.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/_VarNames.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/boolDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/boolDeclaration2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/capCharDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/charDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/charDeclaration2.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/emptyStringDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/intDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/longVarNames.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/manyVariables.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/negIntDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/puncCharDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/stringCarriageReturn.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/stringDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/variables/zeroIntDeclaration.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/fibonacciFullIt.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/fibonacciIterative.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/loopCharCondition.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/loopIntCondition.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/max.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/min.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/rmStyleAdd.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/rmStyleAddIO.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/whileBasic.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/whileBoolFlip.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/whileCount.wacc create mode 100644 src/test/wacc/waccPrograms/valid/while/whileFalse.wacc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3923f1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.metals/ +.bsp/ +.scala-build/ +.vscode/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..f79e2c5 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,16 @@ +stages: + - compile + - test + +compile: + stage: compile + script: + - scala . + +test: + stage: test + script: + - scala test . # This will test everything. Will need to be modified as we add tests + +# More stages will be added later to independently test different phases +# To allow chosen tests to run as we add functionality diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..49f12a5 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +# NOTE: PLEASE DON'T USE THIS MAKEFILE, IT IS FOR LABTS +# it is *much* more efficient to use `scala compile .` trust me, I'm watching you. +all: +# the --server=false flag helps improve performance on LabTS by avoiding +# downloading the build-server "bloop". +# the --jvm system flag helps improve performance on LabTS by preventing +# scala-cli from downloading a whole jdk distribution on the lab machine +# the --force flag ensures that any existing built compiler is overwritten +# the --power flag is needed as `package` is an experimental "power user" feature (NOTE: use this or --assembly if anything goes wrong) +# scala --power package . --server=false --jvm system --force -o wacc-compiler +# you can use --assembly to make it built a self-contained jar, +# scala --power package . --server=false --jvm system --assembly --force -o wacc-compiler +# you can use --native to make it build a native application (requiring Scala Native), +# scala --power package . --server=false --jvm system --native --force -o wacc-compiler +# or you can use --graalvm-jvm-id graalvm-java21 --native-image to build it using graalvm + scala --power package . --server=false --jvm system --graalvm-jvm-id graalvm-java21 --native-image --force -o wacc-compiler + +clean: + scala clean . && rm -f wacc-compiler + +.PHONY: all clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..954e909 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +This is the provided git repository for the WACC compilers lab. You should work +in this repository regularly committing and pushing your work back to GitLab. + +# Provided files/directories + +## src/main + +The src/main directory is where you code for your compiler should go, and just +contains a stub hello world file with a simple calculator inside. + +## src/test +The src/test directory is where you should put the code for your tests, which +can be ran via `scala-cli test .`. The suggested framework is `scalatest`, the dependency +for which has already been included. + +## project.scala +The `project.scala` is the definition of your project's build requirements. By default, +this skeleton has added the latest stable versions of both `scalatest` and `parsley` +to the build: you should check **regularly** to see if your `parsley` needs updating +during the course of WACC! + +## compile + +The compile script can be edited to change the frontend interface to your WACC +compiler. You are free to change the language used in this script, but do not +change its name. + +## Makefile + +Your Makefile should be edited so that running 'make' in the root directory +builds your WACC compiler. Currently running 'make' will call +`scala --power package . --server=false --jvm system --graalvm-jvm-id graalvm-java21 --native-image --force -o wacc-compiler`, producing a file called +`wacc-compiler` +in the root directory of the project. If this doesn't work for whatever reason, there are a few +different alternatives you can try in the makefile. **Do not use the makefile as you're working, it's for labts/CI!** diff --git a/compile b/compile new file mode 100755 index 0000000..5505e42 --- /dev/null +++ b/compile @@ -0,0 +1,9 @@ +#!/bin/bash +# Bash front-end for your compiler. +# You are free to change the language used for this script, +# but do *not* change its name. + +# feel free to adjust to suit the specific internal flags of your compiler +./wacc-compiler "$@" + +exit $? diff --git a/project.scala b/project.scala new file mode 100644 index 0000000..c34b9c6 --- /dev/null +++ b/project.scala @@ -0,0 +1,26 @@ +//> using scala 3.6 +//> using platform jvm + +// dependencies +//> using dep com.github.j-mie6::parsley::5.0.0-M12 +//> using dep com.github.scopt::scopt::4.1.0 +//> using dep com.lihaoyi::os-lib::0.11.4 +//> using test.dep org.scalatest::scalatest::3.2.19 + +// these are all sensible defaults to catch annoying issues +//> using options -deprecation -unchecked -feature +//> using options -Wimplausible-patterns -Wunused:all +//> using options -Yexplicit-nulls -Wsafe-init -Xkind-projector:underscores + +// these will help ensure you have access to the latest parsley releases +// even before they land on maven proper, or snapshot versions, if necessary. +// just in case they cause problems, however, keep them turned off unless you +// specifically need them. +// using repositories sonatype-s01:releases +// using repositories sonatype-s01:snapshots + +// these are flags used by Scala native: if you aren't using scala-native, then they do nothing +// lto-thin has decent linking times, and release-fast does not too much optimisation. +// using nativeLto thin +// using nativeGc commix +// using nativeMode release-fast diff --git a/src/main/wacc/Main.scala b/src/main/wacc/Main.scala new file mode 100644 index 0000000..7a47bf6 --- /dev/null +++ b/src/main/wacc/Main.scala @@ -0,0 +1,109 @@ +package wacc.frontend + +import parsley._ +import scopt.OParser +import wacc.backend.CodeGenerator +import wacc.frontend.semantic._ +import wacc.frontend.semantic.environment._ +import wacc.frontend.syntax.ast.WProgram +import wacc.frontend.syntax.ImportHandler +import wacc.frontend.syntax.parser +import wacc.frontend.syntax.SYNTAX_ERROR_CODE +import wacc.extension.Peephole + +val builder = OParser.builder[Config] +val oParser = { + import builder._ + OParser.sequence( + opt[Unit]('p', "peephole") + .action((_, c) => c.copy(peephole = true)) + .text("Enable peephole mode"), + + opt[Unit]('i', "imports") + .action((_, c) => c.copy(imports = true)) + .text("Enable imports mode"), + + opt[String]('o', "output") + .action((x, c) => c.copy(output = Some(x))) + .text("Output file") + ) +} + +// This is the configuration for ar +case class Config(peephole: Boolean = false, + imports: Boolean = false, + output: Option[String] = None, + input: Option[String] = None) + +// Example script: scala . -- "src/test/wacc/waccPrograms/valid/advanced/hashTable.wacc" +def main(args: Array[String]): Unit = { + + // First handle if the input file is present + val inputFile = args.headOption match { + case Some(file) => file + case _ => "Not Found" + } + + // We handle the remaining flags + val (peepholeFlag, importsFlag, outputFlag) = if (args.length < 2) { + (false, false, None) + } else { + OParser.parse(oParser, args.tail, Config()) match { + case Some(config) => + (config.peephole, config.imports, config.output) + case _ => + (false, false, None) + } + } + println("Hello WACC") + + if (inputFile == "Not Found") { + println("Please enter a valid file path") + } else { + // Compile the code + compile(inputFile, peepholeFlag, importsFlag, outputFlag) + } +} + +/** + * Option to change out how you want to run the Main.scala file + * This won't print out the full program to terminal but will open and close the file safely + * THIS IS THE FUNCTION TO CALL FOR LABTS + * + * @param args + */ +def compile(pathStr: String, peepholeFlag: Boolean, importsFlag: Boolean, outputFlag: Option[String]): Unit = { + parser.parseFile(pathStr) match { + case parsley.Failure(msg) => { + println(s"\nSyntax Error $msg") + sys.exit(SYNTAX_ERROR_CODE) + } + case parsley.Success(progFromSyntax: WProgram) => { + val fileName = os.FilePath(pathStr).last + val progToAnalyse = ImportHandler.apply().addImports(progFromSyntax, optimiseFlag = importsFlag) + analyse(progToAnalyse) match { + case Left(res) => { + println("Completed Frontend ... Transitioning to backend...") + val (prog, fEnv, mEnv) = res + val codeGenerator = new CodeGenerator() + val codeGen = codeGenerator.generate(prog, fEnv, mEnv) + val optimisation = peepholeFlag match { + case true => Peephole.apply().optimize(codeGen) + case false => codeGen + } + val asmCode = optimisation.map(_.emit).mkString("\n") + val outputFile = s"${fileName.replace(".wacc", ".s")}" + val outputPath = os.pwd / os.SubPath(outputFlag.getOrElse("")) / outputFile + os.makeDir.all(outputPath / os.up) + os.write.over(outputPath, asmCode) + println(s"Assembled code generated at ${outputPath / os.up}") + } + + case Right(errs) => { + errs.foreach(err => println(s"\n${err.getMessage(fileName, pathStr)}")) + sys.exit(SEMANTIC_ERROR_CODE) + } + } + } + } +} \ No newline at end of file diff --git a/src/main/wacc/backend/codeGen.scala b/src/main/wacc/backend/codeGen.scala new file mode 100644 index 0000000..5cd3f09 --- /dev/null +++ b/src/main/wacc/backend/codeGen.scala @@ -0,0 +1,754 @@ +package wacc.backend + +import wacc.frontend.syntax.ast._ +import wacc.frontend.semantic.environment._ +import scala.collection.mutable + +class CodeGenerator { + private given allocator: RegisterAllocator = new RegisterAllocator() + private given labelGenerator: LabelGenerator = new LabelGenerator() + + /** + * Main entry for code generation + * Returns the instructions as a list of instructions + */ + def generate(program: WProgram, globalFuncEnv: GlobalFuncEnv, globalMainEnv: GlobalMainEnv): + List[Instruction] = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + given gMainEnv: GlobalMainEnv = globalMainEnv + given gFuncEnv: GlobalFuncEnv = globalFuncEnv + + // Generate code for each function declaration + program.funcs.foreach(func => instructions ++= genFunc(func)) + + // Prepend the main body of the program to current instructions + genMain(program.stats) ++=: instructions + + // Prepend the preamble that has all of the string metadata + preamble() ++=: instructions + + // Append any standard labels found during generation of program + instructions ++= labelGenerator.genWidgets() + + // Add a new line to signify the end of the file + instructions += EOF() + + // Return the final list + instructions.toList + } + + /** + * Generates the preamble for the assembly file + * Returns a list of the instructions + */ + def preamble(): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Add the .data directive + instructions += Directive("data") + + // Add all of the string labels with their info + for ((str, (newStr, label)) <- labelGenerator.getStringLiterals()) { + instructions ++= List( + Comment(s"length of $label", ""), + Directive(s"word ${str.size}", " "), + Label(label), + Directive(s"asciz \"$newStr\"", " ") + ) + } + + // Add remaining preamble info + instructions ++= List( + Directive("align 4"), + Directive("text"), + Directive("global main") + ) + + instructions.toList + } + + /** + * Adds the function label + * Save frame pointer and link register + * Returns a list of the instructions + */ + def funcPrologue(funcLabel: String, _pushCalleeList: List[(String, PhysicalReg)]): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + instructions ++= List( + // Function label + Label(funcLabel), + + // Stack setup (pushing frame pointer onto link register) + Comment("push {fp, lr}"), + STP(FP, LR, SP), + + // Set the frame pointer to the current stack pointer now that fp has been saved + Comment("Set fp to sp"), + MoveOps(FP, SP) + ) + + // Push all of the assigned registers in pushCalleeList using STP, where any odd number of registers + // Would yield a storing of it and the zero register + instructions ++= generatePushInstructions(_pushCalleeList, callingFunc = false) + + // Compute and reserve space for the remaining local variables (Rounded to 16 Bytes to ensure stack alignment) + val _remainingOffset = ((allocator.getNumVariables() - MAX_CALLEE_REGS + 1) & ~1) * EIGHT_BYTES + if (_remainingOffset > 0) { + val remainingOffset = Immediate(_remainingOffset) + instructions += SUB(SP, SP, remainingOffset) + } + + instructions.toList + } + + /** + * Function epilogue: pops all registers that were pushed frame pointer and return + * Returns a list of the instructions + */ + def funcEpilogue(_popCalleeList: List[(String, PhysicalReg)]): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Add back the displacement made to sp if the offset is > 0 + val _remainingOffset = ((allocator.getNumVariables() - MAX_CALLEE_REGS + 1) & ~1) * EIGHT_BYTES + if (_remainingOffset > 0) { + val remainingOffset = Immediate(_remainingOffset) + instructions += ADD(SP, SP, remainingOffset) + } + + // Add the comment for the registers being freed + instructions ++= generatePopInstructions(_popCalleeList.toList, callingFunc = false) + + // Pop the the frame pointer and link register + instructions ++= List( + Comment("pop {fp, lr}"), + LDP(FP, LR, SP), + RET(), + EOF() + ) + + instructions.toList + } + + /* + Generates the push instructions for the registers in the pushList + Returns a list of the instructions + */ + def generatePushInstructions(_pushList: List[(String, Allocated)], callingFunc: Boolean): + List[Instruction] = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Tranform the _pushList to only contain x registers + val pushList = _pushList.collect { + case (id, reg: Reg) => (id, Register(reg.regNumber, X_REG)) + } + + if (!pushList.isEmpty) { + instructions += Comment(s"push {${_pushList.map(_._2.toOperand).mkString(", ")}}") + } + pushList.sliding(2, 2).foreach { pair => + // If there is a pair of registers, then push them together, otherwise, push the singleton + if (pair.length == 2) { + instructions += STP(pair(0)._2, pair(1)._2, SP) + } else { + instructions += STP(pair(0)._2, XZR, SP) + } + } + + if (!pushList.isEmpty && callingFunc) { + // Here we add the stack allocations in a fashion similar to stp for registers so that we can + // change stack allocation easier later on + val spillPushList = _pushList.collect { case (id, spill: Spill) => (id, spill)} + + // Calculate the amount of stack space to allocate to the stack + val _addedStackSpace = ((spillPushList.size + 1) & ~1) * EIGHT_BYTES + val addedStackSpace = Immediate(_addedStackSpace) + + if (_addedStackSpace != 0) { + // Subtract that space from the stack pointer + instructions += SUB(SP, SP, addedStackSpace) + } + + // Push each memory location onto the stack + spillPushList.sliding(2, 2).zipWithIndex.foreach { case (pair, index) => + + // Index is incremented by 1 + val adjustedIndex = index + 1 + + // If there is a pair of spills, then push the rhs first and then the lhs + if (pair.length == 2) { + + // Store the two allocated values + instructions ++= pushMemToStack(pair(1)._2, 2 * adjustedIndex - 1, _addedStackSpace) + instructions ++= pushMemToStack(pair(0)._2, 2 * adjustedIndex, _addedStackSpace) + + // Otherwise, push the zero register and then the other register + } else { + + // The offset to save memory to + val saveOffset = Immediate(_addedStackSpace - (2 * adjustedIndex - 1) * EIGHT_BYTES) + + // Store the two allocated values + instructions += Store(XZR, SP, Some(saveOffset)) + instructions ++= pushMemToStack(pair(0)._2, (2 * adjustedIndex), _addedStackSpace) + } + } + + // Order the registers in the same order of how they'd appear in the stack + val stackList = (toStackList(_pushList)).reverse + + instructions += Comment(s"Our stack has the order of (front of list shows what #0 is): [${stackList.map(_._2.toOperand).mkString(", ")}]") + + // We change the identifier map to point to the stack position of the stored values if we are calling a function + stackList.zipWithIndex.foreach { case ((id, alloc), index) => + + // We create an offset equal to the index of the register times 8 Bytes + val offsetImm = Immediate(EIGHT_BYTES * index) + val stackLocation = Spill(offsetImm, X16) + + allocator.addAllocMapping(id, stackLocation) + } + instructions += Comment("Set up X16 as a temporary second base pointer for the caller saved things") + instructions += MoveOps(X16, SP) + } + instructions.toList + } + + /* + Generates the pop instructions for the registers in the popList + Returns a list of the instructions + */ + + def generatePopInstructions(_popList: List[(String, Allocated)], callingFunc: Boolean): + List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Tranform the _popList to a list only containing x registers + val popList = _popList.collect{case (id, reg: Reg) => (id, Register(reg.regNumber, X_REG))} + + // First remove the memory that was stored away if we were calling a function + if (!popList.isEmpty && callingFunc) { + + val spillPushList = _popList.collect { case (id, spill: Spill) => (id, spill)} + + // Calculate the amount of stack space that was taken away + val _addedStackSpace = ((spillPushList.size + 1) & ~1) * EIGHT_BYTES + val addedStackSpace = Immediate(_addedStackSpace) + + if (_addedStackSpace != 0) { + // Add that space back to the stack pointer + instructions += ADD(SP, SP, addedStackSpace) + } + } + + if (popList.nonEmpty) { + instructions += Comment(s"pop {${_popList.reverse.map(_._2.toOperand).mkString(", ")}}") + + val rest = if (popList.length % 2 == 1) { + instructions += LDP(popList.head._2, XZR, SP) // Pop first register if odd + popList.tail // Process the remaining elements + } else popList + + rest.sliding(2, 2).foreach { pair => + + // As they were pushed in order, to maintain the order, pop in reverse taking right then left + instructions += LDP(pair(1)._2, pair(0)._2, SP) + } + } + + popList.foreach(pair => allocator.freeCallee(pair._2)) + + // We revert the identifier map to point back to the original registers if we called a function + // Note: we use _popList instead of popList as that contains the correct mapping for all allocated values + if (callingFunc) { + _popList.foreach { case (id, alloc) => + allocator.addAllocMapping(id, alloc) + } + } + instructions.toList + } + + def toStackList(list: List[(String, Allocated)]): List[(String, Allocated)] = { + // Order the registers in the same order of how they'd appear in the stack (but reversed) + list.sliding(2, 2).flatMap { pair => + if (pair.length == 2) { + List(pair(1), pair(0)) + } else { + List(("", XZR), pair(0)) + } + }.toList + } + + def pushMemToStack(spill: Spill, num: Int, totalStackSpace: BigInt): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // The offset to save memory to + val saveOffset = Immediate(totalStackSpace - num * EIGHT_BYTES) + + // We must load the contents of the location to x17 and then store that into the stack pointer at the right place + instructions += Load(X17, spill) + instructions += Store(X17, SP, Some(saveOffset)) + instructions.toList + } + + /** + * Generates all of the instructions for the main body block + * Returns a list of the instructions + */ + def genMain(stmts: List[Stmt]) + (using gMainEnv: GlobalMainEnv, gFuncEnv: GlobalFuncEnv): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Generate code for each statement in the main block + given pushCalleeList: mutable.ListBuffer[(String, PhysicalReg)] = mutable.ListBuffer.empty + stmts.foreach(stmt => instructions ++= genStmt(stmt)(using pushCalleeList, gMainEnv, gFuncEnv, List.empty)) + + // Prepend the prologue with the pushCalleeList + funcPrologue("main", pushCalleeList.toList) ++=: instructions + + // Store 0 in return reg as the default exit code if program is fine + instructions += MoveOps(X0, Imm0) + + // Append the epilogue with the popCalleeList(pushCalleeList reversed) + instructions ++= funcEpilogue(pushCalleeList.reverse.toList) + + instructions.toList + } + + /** + * Generates all of the instructions for the functions + * Returns a list of the instructions + */ + def genFunc(func: Func)(using gMainEnv: GlobalMainEnv, gFuncEnv: GlobalFuncEnv): + List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // The id of the function used to query the global function environment + val funcKey = func.id.id + + // Replace # with an underscore for funcLabel + val funcLabel = func.id.id.replaceAll("#", "_") + + // Every function has a different push list to see stored variables + given pushCalleeList: mutable.ListBuffer[(String, PhysicalReg)] = mutable.ListBuffer.empty + + // Save the state of the allocator before setting up function + val prevState = allocator.saveState() + + // Retrieve the parameters of the function + val funcParams = gFuncEnv.lookup(funcKey).getOrElse( + throw new RuntimeException(s"Function $funcKey not found in the environment") + ).params + + given funcRegList: mutable.ListBuffer[(String, Allocated)] = mutable.ListBuffer.empty + funcParams.zipWithIndex.foreach{case (param, index) => + // Add the parameter to the global environment + gMainEnv.add(param) + + // Add the parameter to the function register list + val bitMode = param._type match { + case (IntType | CharType | BoolType) => W_REG + case _ => X_REG + } + + val paramAlloc = index match { + + // If the index is between 0 and 7, then create a parameter register for it + case paramReg if paramReg < MAX_PARAM_REGS => Register(s"$paramReg", bitMode) + + // Otherwise, create a space in memory for it + case nonReg => + + // Calculate the stack offset for the non register parameter + val offset = (nonReg - MAX_PARAM_REGS) * EIGHT_BYTES + + // Adjust the offset by 16 Bytes to account for fp lr push onto stack + val adjustedOffset = BigInt(offset) + SIXTEEN_BYTES + val stackOffset = Immediate(adjustedOffset) + + // Create a stack allocation with FP as the base pointer as this is how to access the + // variable in the function + Spill(stackOffset, FP) + } + + // Adding the param's name with its arg register to the funcRegList + val listEntryFunc = (param.uniqueName, paramAlloc) + funcRegList += listEntryFunc + + // Also add the allocation to the callee push list + val listEntryCallee = (param.uniqueName, paramAlloc) + listEntryCallee match { + case (identifier, physicalReg: PhysicalReg) => + val entry = (identifier, physicalReg) + pushCalleeList += entry + case _ => () + } + + // Create the linkage between an id and its paramAlloc + allocator.addAllocMapping(param.uniqueName, paramAlloc) + } + + // Generate code for each statement in the function block + func.body.foreach(stmt => instructions ++= + genStmt(stmt)(using pushCalleeList, gMainEnv, gFuncEnv, funcRegList.toList)) + + // Prepend the prologue with the pushCalleeList + funcPrologue(funcLabel, pushCalleeList.toList) ++=: instructions + + // Replace all instances of a comment called "EPILOGUE" with the epilogue of the function + // At this point, we have all callee registers known to us + val finalInstructions = instructions.flatMap { + case Comment("EPILOGUE", "") => funcEpilogue(pushCalleeList.reverse.toList) + case other => List(other) + } + + // Restore the state to allow for callees to run as intended + allocator.restoreState(prevState) + + finalInstructions.toList + } + + /** + * Generates code for a statement + * Returns a list of instructions + */ + def genStmt(stmt: Stmt) + (using pushCalleeList: mutable.ListBuffer[(String, PhysicalReg)], gMainEnv: GlobalMainEnv, + gFuncEnv: GlobalFuncEnv, funcRegList: List[(String, Allocated)]): List[Instruction] = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + stmt match { + case Skip(_) => + + // We don't have any instructions here + instructions += Comment("SKIP") + instructions.toList + + case Declare(_, t, ident, rhs) => + instructions += Comment("DECLARE BEGINS HERE") + + // Evaluate the right-hand side + val (rhsInstr, rhsAlloc) = genRhs(rhs, this) + instructions ++= rhsInstr + + // Allocate a callee allocation for this ident + val calleeAlloc = rhsType(rhs)(using gMainEnv, gFuncEnv) match { + + // Ints, Chars, and Bools all use W_REG register bit-modes + case (IntType | CharType | BoolType) => allocator.allocateCallee(W_REG) + case _ => allocator.allocateCallee(X_REG) + } + + // Add the allocation to pushCalleeList (Will create STP instructions to store to the stack) + val listEntryCallee = (ident.id, calleeAlloc) + listEntryCallee match { + case (identity, physicalReg: PhysicalReg) => + val entry = (identity, physicalReg) + pushCalleeList += entry + case _ => () + } + + // Store the value of the rhs to the lhs + // Check on the whether the operand is on the stack or a register + instructions += Move(calleeAlloc, rhsAlloc) + + // Add the allocation to the variable's map of vars to maps + allocator.addAllocMapping(ident.id, calleeAlloc) + + // Free the temp register + allocator.freeTemp(rhsAlloc) + + rhs match { + case Call(_, id, args) => + + // If the rhs is a function call, free all of the callee registers + instructions ++= generatePopInstructions(funcRegList.reverse, callingFunc = true) + case _ => + } + + instructions.toList + + case Assign(_, lhs, rhs) => + instructions += Comment("ASSIGN BEGINS HERE") + + // Create w/x8 register using allocTemp as this is at the front of the temp pool (only for ArrayElems) + val bufferReg8 = lhsType(lhs)(using gMainEnv) match { + case (IntType | CharType | BoolType) => allocator.allocateTemp(W_REG) + case _ => allocator.allocateTemp(X_REG) + } + + // Only free the lhs if it's not an arrayElem + lhs match { + case ArrayElem(_, _, _) => () + case _ => allocator.freeTemp(bufferReg8) + } + + val (rhsInstr, rhsAlloc) = genRhs(rhs, this) + instructions ++= rhsInstr + + val newRhsAlloc = rhs match { + case Call(_, id, args) => + + // Set up the function scrath and return registers according to the lhs type + val (functionScratchReg, returnReg) = lhsType(lhs)(using gMainEnv) match { + case (IntType | CharType | BoolType) => (W16, W0) + case _ => (X16, X0) + } + + // Move the output to a function scratch register + instructions += MOV(functionScratchReg, returnReg) + + // Free all of the callee registers + instructions ++= generatePopInstructions(funcRegList.reverse, callingFunc = true) + + functionScratchReg + + case _ => rhsAlloc + } + + // Get the lhs to move the rhs into + val (lhsInstr, lhsAlloc) = genLhsForUsing(lhs, instructions) + instructions ++= lhsInstr + + // Make the lhsMove now + // Performs the moving of the rhs to the lhs which handles edge cases (ArrayElem, PairElem) + instructions ++= lhsMove(lhs, lhsAlloc, newRhsAlloc, bufferReg8) + + // Free all temp allocations + allocator.freeTemp(newRhsAlloc) + allocator.freeTemp(rhsAlloc) + allocator.freeTemp(lhsAlloc) + allocator.freeTemp(bufferReg8) + + instructions.toList + + case pStmt: PrintBase => + instructions += Comment("PRINT BEGINS HERE") + + // Push registers onto the stack to save their value + instructions ++= generatePushInstructions(funcRegList, true) + + val (exprInstr, exprAlloc) = genExpr(pStmt.expr) + + // The bit mode of the return register (x/w 0) will be determined by the expression's type + val returnReg = exprType(pStmt.expr)(using gMainEnv) match { + case (IntType | CharType | BoolType) => W0 + case _ => X0 + } + instructions ++= exprInstr + instructions += MoveOps(returnReg, exprAlloc) + + // Check for the type of the expression here + exprType(pStmt.expr)(using gMainEnv) match { + case BoolType => + instructions += BL(_printb) + case CharType => + instructions += BL(_printc) + case IntType => + instructions += BL(_printi) + + // Can print Char arrays as a string + case (StrType | ArrayType(CharType)) => + instructions += BL(_prints) + + // All of the other types require _printp + case _ => + instructions += BL(_printp) + } + + // Add the branch to _println for a Println statement + if (pStmt.isInstanceOf[Println]) { + instructions += BL(_println) + } + + // Free allocation + allocator.freeTemp(exprAlloc) + + // Pop off the saved registers from the stack + instructions ++= generatePopInstructions(funcRegList.reverse.toList, callingFunc = true) + instructions.toList + + case Read(_, lhs) => + instructions += Comment("READ BEGINS HERE") + + // Save argument registers + instructions ++= generatePushInstructions(funcRegList, callingFunc = true) + + // Create w8 register using allocTemp as this is at the front of the temp pool + val bufferReg8 = allocator.allocateTemp(W_REG) + val (lhsInstr, lhsAlloc) = genLhs(lhs) + + // Ints and Chars will always use W_REG bit-mode registers + instructions ++= lhsInstr + lhs match { + case _: PairElem => + instructions ++= List( + Compare(lhsAlloc, Imm0), + BCond(EQ, _errNull), + Load(X0, lhsAlloc) + ) + case _ => instructions += Move(W0, lhsAlloc) + } + + // Check for the type of the lhs here + lhsType(lhs)(using gMainEnv) match { + case CharType => + instructions += BL(_readc) + + case IntType => + instructions += BL(_readi) + + case _ => + instructions += Comment("UNKNOWN TYPE IN READ") + } + + // Finish register manipulation + // Depending on the type of the lhs, invoke the correct next move + // Ints and Chars will always use W_REG bit-mode registers + instructions += MoveOps(W16, W0) + + // Free the lhsAlloc before reloading lhs + allocator.freeTemp(lhsAlloc) + + // Reload the lhs so that we get the correct address + val (reloadInstrs, reloadLhs) = genLhsForUsing(lhs, instructions) + instructions ++= reloadInstrs + instructions ++= lhsMove(lhs, reloadLhs, W16, bufferReg8) + + allocator.freeTemp(bufferReg8) + allocator.freeTemp(reloadLhs) + + // Pop off argument registers + instructions ++= generatePopInstructions(funcRegList.reverse.toList, callingFunc = true) + instructions.toList + + case Exit(_, expr) => + instructions += Comment("EXIT BEGINS HERE") + + // Push registers onto the stack to save their value + instructions ++= generatePushInstructions(funcRegList, true) + + // For an exit statement, evaluate the expression and call the exit routine + val (exprInstr, exprAlloc) = genExpr(expr) + instructions ++= exprInstr + + // The expression is always an int so we use the W_REG bit-mode + instructions += MoveOps(W0, exprAlloc) + instructions += BL(exit) + + // Pop off argument registers + instructions ++= generatePopInstructions(funcRegList.reverse.toList, callingFunc = true) + + allocator.freeTemp(exprAlloc) + instructions.toList + + case Return(_, expr) => + instructions += Comment("RETURN BEGINS HERE") + val (exprInstr, exprAlloc) = genExpr(expr) + + // The bit mode of the return register (x/w 0) will be determined by the expression's type + val returnReg = exprType(expr)(using gMainEnv) match { + case (IntType | CharType | BoolType) => W0 + case _ => X0 + } + instructions ++= exprInstr + instructions += MoveOps(returnReg, exprAlloc) + + // We mark this section as needing to have an epilogue + // It is filled in later in the genFunc method + instructions += Comment("EPILOGUE", "") + + allocator.freeTemp(exprAlloc) + instructions.toList + + case If(_, cond, thenStats, elseStats) => + instructions += Comment("IF BEGINS HERE") + + // Generate fresh labels for then and end case. Else case would go right after label + val thenLabel = labelGenerator.freshLabel() + val endLabel = labelGenerator.freshLabel() + + // Generate code for the condition, ending with a Compare instruction + val (condInstr, branchFlag) = genCond(cond) + instructions ++= condInstr + + // Branch to the then block if the condition is true + instructions += BCond(branchFlag, thenLabel) + + // Continue onto generating code for the else block + elseStats.foreach(stmt => instructions ++= genStmt(stmt)) + + // Branch to the end label after finishing else block + instructions += B(endLabel) + + // Label and instructions for then + instructions += Label(thenLabel) + thenStats.foreach(stmt => instructions ++= genStmt(stmt)) + + // End of the if statement + instructions += Label(endLabel) + + instructions.toList + + case While(_, cond, body) => + instructions += Comment("WHILE BEGINS HERE") + + // Generate fresh labels for the loop + val condLabel = labelGenerator.freshLabel() + val bodyLabel = labelGenerator.freshLabel() + + // Branch to the condition check + instructions += B(condLabel) + + // Generate code for the loop body + instructions += Label(bodyLabel) + body.foreach(stmt => instructions ++= genStmt(stmt)) + + // After the body, check the condition again + instructions += B(condLabel) + + // Generate code for the condition check + instructions += Label(condLabel) + val (condInstr, condFlag) = genCond(cond) + instructions ++= condInstr + instructions += BCond(condFlag, bodyLabel) + + // End of the loop just continues + + instructions.toList + + // Generates each statement in the block + case Block(_, stmts) => stmts.flatMap(genStmt) + + case Free(_, expr: Rhs) => + val (exprInstr, exprAlloc) = genRhs(expr, this) + + // Save argument registers + instructions ++= generatePushInstructions(funcRegList, callingFunc = true) + instructions ++= exprInstr + + exprType(expr)(using gMainEnv) match { + case _: ArrayType => + instructions ++= List( + Comment("array pointers are shifted forward by 4 bytes, " + + "so correct it back to original pointer before free"), + SUB(exprAlloc, exprAlloc, Imm4), + MoveOps(X0, exprAlloc), + BL(free) + ) + case _: (PairType | PairPlaceholder.type | Any) => + instructions += MoveOps(X0, exprAlloc) + instructions += BL(_freepair) + } + + // Pop off argument registers + instructions ++= generatePopInstructions(funcRegList.reverse.toList, callingFunc = true) + + allocator.freeTemp(exprAlloc) + instructions.toList + } + } + +} diff --git a/src/main/wacc/backend/expressionGen.scala b/src/main/wacc/backend/expressionGen.scala new file mode 100644 index 0000000..4c60c16 --- /dev/null +++ b/src/main/wacc/backend/expressionGen.scala @@ -0,0 +1,449 @@ +package wacc.backend + +import wacc.frontend.syntax.ast._ +import wacc.frontend.semantic.environment._ +import scala.collection.mutable + +/** + * Generates code for an expression + * Returns a tuple: (instructions, allocated operand holding the result) +*/ +def genExpr(expr: Expr) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], PhysicalReg) = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // The bit-mode of the temp register depends on the type of the expression + val tempAlloc = exprType(expr) match { + case (IntType | CharType | BoolType) => allocator.allocateTemp(W_REG) + case _ => allocator.allocateTemp(X_REG) + } + + expr match { + case IntLiter(_, value) => + val valueImm = Immediate(value) + instructions += MoveOps(tempAlloc, valueImm) + (instructions.toList, tempAlloc) + + case BoolLiter(_, _value) => + val value = if (_value) 1 else 0 + val valueImm = Immediate(value) + instructions += MoveOps(tempAlloc, valueImm) + (instructions.toList, tempAlloc) + + case CharLiter(_, value) => + val valueImm = Immediate(value.toInt) + instructions += MoveOps(tempAlloc, valueImm) + (instructions.toList, tempAlloc) + + case StrLiter(_, value) => + val strLabel = labelGenerator.freshLabel(value) + instructions += ADRP(tempAlloc, strLabel) + instructions += ADD(tempAlloc, tempAlloc, LabelOp(strLabel)) + (instructions.toList, tempAlloc) + + case PairLiter(_) => + instructions += MoveOps(tempAlloc, Imm0) + (instructions.toList, tempAlloc) + + case Ident(_, id) => + val _idAlloc = allocator.getAllocMapping(id) + + // Make the bit-mode of the idAlloc the same as the bitmode of the tempAlloc + val idAlloc = _idAlloc match { + case reg: PhysicalReg => tempAlloc match { + case PhysicalReg(_, X_REG, _) => PhysicalReg(reg.regNumber, X_REG, reg.pool) + case PhysicalReg(_, other, _) => PhysicalReg(reg.regNumber, W_REG, reg.pool) + } + case spill => spill + } + instructions += Move(tempAlloc, idAlloc) + (instructions.toList, tempAlloc) + + case Add(lhs, rhs) => + allocator.freeTemp(tempAlloc) + genArithOp(lhs, rhs, ADDS.apply) + case Sub(lhs, rhs) => + allocator.freeTemp(tempAlloc) + genArithOp(lhs, rhs, SUBS.apply) + case Mul(lhs, rhs) => + + // Restore the register to make efficient use of registers + allocator.restore() + val (lhsInstr, lhsAlloc) = genExpr(lhs) + val (rhsInstr, rhsAlloc) = genExpr(rhs) + + // Need an x bit-mode register for detecting overflow errors + val xLhs = PhysicalReg(lhsAlloc.regNumber, X_REG, lhsAlloc.pool) + + instructions ++= lhsInstr + instructions ++= rhsInstr + instructions ++= List( + SMULL(xLhs, lhsAlloc, rhsAlloc), + Compare(xLhs, lhsAlloc ,Some(SXTW())), + BCond(NE, _errOverflow) + ) + + allocator.freeTemp(rhsAlloc) + (instructions.toList, lhsAlloc) + + // Handles Division and Modulo operations + case divOp: DivOp => + val (lhs, rhs) = (divOp.lhs, divOp.rhs) + allocator.restore() + val (lhsInstr, lhsAlloc) = genExpr(lhs) + val (rhsInstr, rhsAlloc) = genExpr(rhs) + instructions ++= lhsInstr + instructions ++= rhsInstr + instructions ++= List( + Compare(rhsAlloc, Imm0), + BCond(EQ, _errDivZero), + ) + divOp match { + case _: Div => instructions += SDIV(lhsAlloc, lhsAlloc, rhsAlloc) + case _: Mod => + instructions += SDIV(W17, lhsAlloc, rhsAlloc) + instructions += MSUB(lhsAlloc, W17, rhsAlloc, lhsAlloc) + } + allocator.freeTemp(rhsAlloc) + (instructions.toList, lhsAlloc) + + case And(lhs, rhs) => genBoolOp(lhs, rhs, NE) + case Or(lhs, rhs) => genBoolOp(lhs, rhs, EQ) + case compOp: CompOp => genComparison(compOp.lhs, compOp.rhs, compOp) + case chr: Chr => + val (chrInstr, chrAlloc) = genUnaryOp(chr.expr, chr) + val xChr = chrAlloc match { + case PhysicalReg(regNumber, _ , pool) => PhysicalReg(regNumber, X_REG, pool) + } + instructions ++= chrInstr + val testVal = -128 + val tstImm = Immediate(testVal) + instructions ++= List( + TST(chrAlloc, tstImm), + CSEL(X1, xChr, X1, NE), + BCond(NE, _errBadChar) + ) + (instructions.toList, chrAlloc) + + case neg: Neg => + val (negInstr, negAlloc) = genUnaryOp(neg.expr, neg) + instructions ++= negInstr + instructions += BCond(VS, _errOverflow) + (instructions.toList, negAlloc) + + case unOp: UnOp => genUnaryOp(unOp.expr, unOp) + + case IfExpr(pos, cond, thenBranch, elseBranch) => + val thenLabel = labelGenerator.freshLabel() + val endLabel = labelGenerator.freshLabel() + val (condInstr, branchFlag) = genCond(cond) + + instructions ++= condInstr + + // Branch to the then block if the condition is true + instructions += BCond(branchFlag, thenLabel) + val resultReg = exprType(thenBranch) match { + case (IntType | CharType | BoolType) => allocator.allocateTemp(W_REG) + case _ => allocator.allocateTemp(X_REG) + } + val (elseInstr, elseAlloc) = genExpr(elseBranch) + instructions ++= elseInstr + instructions += Move(resultReg, elseAlloc) + instructions += B(endLabel) + + instructions += Label(thenLabel) + val (thenInstr, thenAlloc) = genExpr(thenBranch) + instructions ++= thenInstr + instructions += Move(resultReg, thenAlloc) + instructions += B(endLabel) + + instructions += Label(endLabel) + + (instructions.toList, resultReg) + + case PairElem(_, selector, lhs: Expr) => + allocator.restore() + val (exprInstr, exprAlloc) = genExpr(lhs) + val xExpr = PhysicalReg(exprAlloc.regNumber, X_REG, exprAlloc.pool) + instructions ++= exprInstr + instructions += Compare(exprAlloc, Imm0) + instructions += BCond(EQ, _errNull) + selector match { + case Fst => instructions += LDR(xExpr, exprAlloc) + case Snd => instructions += LDR(xExpr, exprAlloc, Some(Imm8)) + } + + // We use knowledge of the bitmode from tempAlloc to get the right bitmode for return + val resultReg = PhysicalReg(xExpr.regNumber, tempAlloc.bitMode, xExpr.pool) + (instructions.toList, resultReg) + + case ArrayElem(_, id, indices) => + // Get the array type of the identifier we are accessing + var curType = exprType(id) match { + case ArrayType(innerType) => innerType + case _type => _type + } + + val xTemp = PhysicalReg(tempAlloc.regNumber, X_REG, tempAlloc.pool) + + for ((indexExpr, i) <- indices.zipWithIndex) { + instructions += Comment(s"curType = $curType, indexExpr = $indexExpr, i = $i") + + // If i != last index, then we have to store our previous value (so must be an x bit-value) + if (i != 0) { + instructions += Comment(s"push {${xTemp}}") + instructions += STP(xTemp, XZR, SP) + } + + // Generate code for the index expression + val (indexInstr, indexAlloc) = genExpr(indexExpr) + instructions ++= indexInstr + instructions += MoveOps(W17, indexAlloc) + + allocator.freeTemp(indexAlloc) + + // If we're not on the first iteration, pop x7 back + if (i != 0) { + instructions += Comment("pop {x7}") + instructions += LDP(X7, XZR, SP) + } else { + val (idInstr, idAlloc) = genExpr(id) + instructions ++= idInstr + instructions += MoveOps(X7, idAlloc) + + allocator.freeTemp(idAlloc) + } + + // Call the appropriate array-load function + instructions += BL(_arrLoad(s"${sizeof(curType)}")) + + curType match { + case ArrayType(_) => instructions += MoveOps(xTemp, X7) + case _ => + val reg7 = tempAlloc match { + case reg: Reg => reg.bitMode match { + case W_REG => W7 + case X_REG => X7 + + // This case shouldn't happen (Maybe call it a random register) + case _ => Register("Should know type of reg7", ' ') + } + } + instructions += MoveOps(tempAlloc, reg7) + } + + curType match { + case ArrayType(innerType) => curType = innerType + case _ => () + } + } + + (instructions.toList, tempAlloc) + } +} + +/** + * Generates assembly for a binary operation + */ +def genBinaryOp(lhs: Expr, rhs: Expr, op: (Reg, Operand, Operand) => Instruction) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], PhysicalReg) = { + + // Allocate a w register as all binary operators operate on types of 4 bytes + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Restore the register to make efficient use of registers + val (lhsAlloc, rhsAlloc) = + if (exprWeight(lhs) > exprWeight(rhs)) { + getEvaluatedExpr(lhs, rhs, instructions, true) + } else { + getEvaluatedExpr(rhs, lhs, instructions, false) + } + instructions += op(lhsAlloc, lhsAlloc, rhsAlloc) + + allocator.freeTemp(rhsAlloc) + (instructions.toList, lhsAlloc) +} + +/** + * Evaluates an expression by taking the lhs and rhs + * Uses the takeLeft parameter to dictate the order that we return the allocations with + */ +def getEvaluatedExpr(lhs: Expr, rhs: Expr, instructions: mutable.ListBuffer[Instruction], takeLeft: Boolean) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (PhysicalReg, PhysicalReg) = { + + val (lhsInstr, lhsAlloc) = genExpr(lhs) + val (rhsInstr, rhsAlloc) = genExpr(rhs) + + instructions ++= lhsInstr + instructions ++= rhsInstr + + if (takeLeft) { + (lhsAlloc, rhsAlloc) + } else { + (rhsAlloc, lhsAlloc) + } +} + +def exprWeight(expr: Expr): Int = expr match { + case binOp: BinOp => Math.max(exprWeight(binOp.lhs), exprWeight(binOp.rhs)) + 1 + case _ => 0 +} + +def genArithOp(lhs: Expr, rhs: Expr, op: (Reg, Operand, Operand) => Instruction) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], PhysicalReg) = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val (addsInstr, addsAlloc) = genBinaryOp(lhs, rhs, op) + instructions ++= addsInstr + instructions += BCond(VS, _errOverflow) + (instructions.toList, addsAlloc) +} + +/** + * Generates assembly for a unary operator + */ +def genUnaryOp(expr: Expr, unOp: UnOp) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], PhysicalReg) = { + + // Allocate a w register as all unary operators operate on types of 4 bytes + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val op = unOp match { + case _: Not => EOR.apply(_, _, Imm1) + case _: Len => LDUR.apply + case _: Neg => NEGS.apply + case _: (Ord | Chr) => MoveOps.apply + } + + // Restores a temp register as they will collapse onto each other + allocator.restore() + val (exprInstr, exprAlloc) = genExpr(expr) + + // Free the temp register as it will be the same as the temp + allocator.freeTemp(exprAlloc) + val tempAlloc = allocator.allocateTemp(W_REG) + + instructions ++= exprInstr + instructions += op(tempAlloc, exprAlloc) + + (instructions.toList, tempAlloc) +} + +/** + * Generates assembly for a comparison expression + */ +def genComparison(lhs: Expr, rhs: Expr, compOp: CompOp) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], PhysicalReg) = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val cond = compOp match { + case Gt(lhs, rhs) => GT + case Geq(lhs, rhs) => GE + case Lt(lhs, rhs) => LT + case Leq(lhs, rhs) => LE + case Eq(lhs, rhs) => EQ + case Neq(lhs, rhs) => NE + } + + // Restore a temp register to keep register use efficient + allocator.restore() + val (lhsInstr, lhsAlloc) = genExpr(lhs) + val (rhsInstr, rhsAlloc) = genExpr(rhs) + + // We use a w register as we are dealing with booleans + val wLhs = PhysicalReg(lhsAlloc.regNumber, W_REG, lhsAlloc.pool) + + instructions ++= lhsInstr + instructions ++= rhsInstr + instructions += Compare(lhsAlloc, rhsAlloc) + instructions += CSET(wLhs, cond) + + allocator.freeTemp(rhsAlloc) + (instructions.toList, wLhs) +} + +def genBoolOp(lhs: Expr, rhs: Expr, cond: Flag) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], PhysicalReg) = { + allocator.restore() + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val (lhsInstr, lhsAlloc) = genExpr(lhs) + val boolLabel = labelGenerator.freshLabel() + instructions ++= lhsInstr + instructions ++= List( + Compare(lhsAlloc, Imm1), + BCond(cond, boolLabel) + ) + allocator.freeTemp(lhsAlloc) + val (rhsInstr, rhsAlloc) = genExpr(rhs) + instructions ++= rhsInstr + instructions ++= List( + Compare(rhsAlloc, Imm1), + Label(boolLabel), + CSET(rhsAlloc, EQ) + ) + (instructions.toList, rhsAlloc) +} + +/** + * Generates code for a condition + * Function guarantees that Compare instruction is generated, to be used by caller function + * Returns a tuple: (instructions, branch condition) +*/ +def genCond(cond: Expr) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], Flag) = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + cond match { + case Neq(lhs, rhs) => (generateOpCond(lhs, rhs), NE) + case Eq(lhs, rhs) => (generateOpCond(lhs, rhs), EQ) + case Not(expr) => (generateOpCond(expr, BoolLiter(IGNORE, true)), NE) + case Gt(lhs, rhs) => (generateOpCond(lhs, rhs), GT) + case Lt(lhs, rhs) => (generateOpCond(lhs, rhs), LT) + case Geq(lhs, rhs) => (generateOpCond(lhs, rhs), GE) + case Leq(lhs, rhs) => (generateOpCond(lhs, rhs), LE) + + case expr: (Ident | And | Or) => + val (exprInstr, exprAlloc) = genExpr(expr) + instructions ++= exprInstr + instructions += Compare(exprAlloc, Imm1) + allocator.freeTemp(exprAlloc) + (instructions.toList, EQ) + + case x: BoolLiter => + val (lhsInstr, lhsAlloc) = genExpr(cond) + + instructions ++= lhsInstr + instructions += Compare(lhsAlloc, Imm1) + + allocator.freeTemp(lhsAlloc) + (instructions.toList, EQ) + case _ => (List(Comment("CONDITION NOT CREATED")), FAILEDFLAG) + } +} + +/** + * generates instructions for a comparison expression + */ +def generateOpCond(lhs: Expr, rhs: Expr) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + List[Instruction] = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val (lhsInstr, lhsAlloc) = genExpr(lhs) + val (rhsInstr, rhsAlloc) = genExpr(rhs) + instructions ++= lhsInstr + instructions ++= rhsInstr + instructions += Compare(lhsAlloc, rhsAlloc) + + allocator.freeTemp(lhsAlloc) + allocator.freeTemp(rhsAlloc) + instructions.toList +} \ No newline at end of file diff --git a/src/main/wacc/backend/instructions.scala b/src/main/wacc/backend/instructions.scala new file mode 100644 index 0000000..eae8630 --- /dev/null +++ b/src/main/wacc/backend/instructions.scala @@ -0,0 +1,471 @@ +package wacc.backend + +import scala.collection.mutable + +val MIN_IMM_VAL = -256 +val MAX_IMM_VAL = 255 + +/** + * A representation for AArch64 assembly instructions. + * Each instruction is wrapped in a case class that knows how to emit its string. + */ +sealed trait Instruction { + def emit: String +} + +// -------------------------- +// Basic instruction classes +// -------------------------- + +// Write a comment with default indentation +case class Comment(name: String, tabs: String = " ") extends Instruction { + override def emit: String = s"$tabs// $name" +} + +case class Directive(name: String, tabs: String = "") extends Instruction { + override def emit: String = s"$tabs.$name" +} + +case class Label(name: String) extends Instruction { + override def emit: String = s"$name:" +} + +// -------------------------- +// Data–movement instructions +// -------------------------- + +// MOV now takes a destination register and a source operand. +case class MOV(dest: Reg, src: Operand) extends Instruction { + override def emit: String = s" mov ${dest.toOperand}, ${src.toOperand}" +} + +case class MoveOps(dest: Reg, src: Operand) extends Instruction { + override def emit: String = getInstrs().map(_.emit).mkString("\n") + def getInstrs(): List[Instruction] = + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val bitMask = 0xFFFF + val shift16 = 16 + src match { + case _: Reg => instructions += MOV(dest, src) + case _: LabelOp => () // Label Op shouldn't be in the move (at least not for now?) + case Immediate(value) => + val valInt = value.toInt + valInt match { + // Handles immediate values that are greater than 0xFFFF and breals them into two parts + case _ if (valInt >= 0 && valInt < bitMask) => instructions += MOV(dest, src) + case _ => + val immOne = Immediate((valInt & bitMask)) + val immTwo = Immediate(((valInt >> shift16) & bitMask)) + instructions += MOV(dest, immOne) + instructions += MOVK(dest, immTwo, LSL(Imm16)) + } + } + instructions.toList +} + +case class MOVK(dest: Reg, src: Operand, shift: Shift) extends Instruction { + override def emit: String = s" movk ${dest.toOperand}, ${src.toOperand}, ${shift.emit}" +} + +// -------------------------- +// Arithmetic instructions +// -------------------------- + +case class ADD(dest: Reg, op1: Operand, op2: Operand) extends Instruction { + override def emit: String = s" add ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}" +} + +case class ADDS(dest: Reg, op1: Operand, op2: Operand) extends Instruction { + override def emit: String = s" adds ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}" +} + +case class SUB(dest: Reg, op1: Operand, op2: Operand) extends Instruction { + override def emit: String = s" sub ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}" +} + +case class SUBS(dest: Reg, op1: Operand, op2: Operand) extends Instruction { + override def emit: String = s" subs ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}" +} + +case class SMULL(dest: Reg, op1: Operand, op2: Operand) extends Instruction { + override def emit: String = s" smull ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}" +} + +case class SDIV(dest: Reg, op1: Operand, op2: Operand) extends Instruction { + override def emit: String = s" sdiv ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}" +} + +case class MSUB(dest: Reg, op1: Operand, op2: Operand, op3: Operand) extends Instruction { + override def emit: String = s" msub ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}, ${op3.toOperand}" +} + +case class NEGS(dest: Reg, src: Operand) extends Instruction { + override def emit: String = s" negs ${dest.toOperand}, ${src.toOperand}" +} + +// -------------------------- +// Bitwise instructions +// -------------------------- + +case class EOR(dest: Reg, op1: Operand, op2: Operand) extends Instruction { + override def emit: String = s" eor ${dest.toOperand}, ${op1.toOperand}, ${op2.toOperand}" +} + +// -------------------------- +// Address instructions +// -------------------------- + +case class ADRP(dest: Reg, label: String) extends Instruction { + override def emit: String = s" adrp ${dest.toOperand}, $label" +} + +case class ADR(dest: Reg, label: String) extends Instruction { + override def emit: String = s" adr ${dest.toOperand}, $label" +} + +// -------------------------- +// Comparison and flag-setting +// -------------------------- + +case class SXTW() extends Instruction { + override def emit: String = "sxtw" +} + +case class CMP(op1: Operand, op2: Operand, _sxtw: Option[SXTW] = None) extends Instruction { + override def emit: String = _sxtw match { + case None => s" cmp ${op1.toOperand}, ${op2.toOperand}" + case Some(sxtw) => s" cmp ${op1.toOperand}, ${op2.toOperand}, ${sxtw.emit}" + } +} + +case class CSET(dest: Reg, condition: Flag) extends Instruction { + override def emit: String = s" cset ${dest.toOperand}, ${condition.emit}" +} + +case class TST(reg: Reg, op: Operand) extends Instruction { + override def emit: String = s" tst ${reg.toOperand}, ${op.toOperand}" +} + +// -------------------------- +// Branch instructions +// -------------------------- + +case class B(label: String) extends Instruction { + override def emit: String = s" b $label" +} + +case class BL(label: String, _ignore: String = "") extends Instruction { + override def emit: String = s" bl $label" +} + +object BL { + def apply(label: String)(using labelGenerator: LabelGenerator): BL = { + labelGenerator.addWidget(label) + new BL(label) + } +} + +case class BCond(flag: Flag, label: String, _ignore: String = "") extends Instruction { + override def emit: String = s" b.${flag.emit} $label" +} + +object BCond { + def apply(flag: Flag, label: String)(using labelGenerator: LabelGenerator): BCond = { + labelGenerator.addWidget(label) + new BCond(flag, label) + } +} + +case class CBZ(reg: Reg, label: String) extends Instruction { + override def emit: String = s" cbz ${reg.toOperand}, $label" +} + +case class CSEL(dest: Reg, src1: Reg, src2: Reg, condition: Flag) extends Instruction { + override def emit: String = s" csel ${dest.toOperand}, ${src1.toOperand}, ${src2.toOperand}, ${condition.emit}" +} + +// -------------------------- +// Load/store instructions +// -------------------------- + +// A shift operation +sealed trait Shift { + def emit: String +} +case class LSL(amount: Immediate) extends Shift { + override def emit: String = s"lsl ${amount.toOperand}" +} + +// LDR +case class LDR(dest: Reg, base: Reg, offset: Option[Operand] = None, shift: Option[Shift] = None) extends Instruction { + override def emit: String = (offset, shift) match { + case (None, None) => s" ldr ${dest.toOperand}, [${base.toOperand}]" + case (Some(off), None) => s" ldr ${dest.toOperand}, [${base.toOperand}, ${off.toOperand}]" + case (Some(off), Some(sft)) => s" ldr ${dest.toOperand}, [${base.toOperand}, ${off.toOperand}, ${sft.emit}]" + case _ => s" ldr ${dest.toOperand}, [${base.toOperand}]" + } +} + +// LDUR +case class LDUR(dest: Reg, base: Operand) extends Instruction { + override def emit: String = s" ldur ${dest.toOperand}, [${base.toOperand}, #-4]" +} + +// LDP +case class LDP(dest1: Reg, dest2: Reg, base: Reg) extends Instruction { + override def emit: String = s" ldp ${dest1.toOperand}, ${dest2.toOperand}, [${base.toOperand}], #16" +} + +// LDRB +case class LDRB(dest: Reg, base: Reg, offset: Operand) extends Instruction { + override def emit: String = s" ldrb ${dest.toOperand}, [${base.toOperand}, ${offset.toOperand}]" +} + +// STP +case class STP(src1: Reg, src2: Reg, base: Reg, offset: Option[Operand] = None) extends Instruction { + override def emit: String = offset match { + case None => s" stp ${src1.toOperand}, ${src2.toOperand}, [${base.toOperand}, #-16]!" + case Some(off) => s" stp ${src1.toOperand}, ${src2.toOperand}, [${base.toOperand}, ${off.toOperand}]!" + } +} + +// STR +case class STR(src: Reg, base: Reg, offset: Option[Operand] = None, shift: Option[Shift] = None) extends Instruction { + override def emit: String = (offset, shift) match { + case (None, None) => s" str ${src.toOperand}, [${base.toOperand}]" + case (Some(off), None) => s" str ${src.toOperand}, [${base.toOperand}, ${off.toOperand}]" + case (Some(off), Some(sft)) => s" str ${src.toOperand}, [${base.toOperand}, ${off.toOperand}, ${sft.emit}]" + case _ => s" str ${src.toOperand}, [${base.toOperand}]" + } +} + +// STRB +case class STRB(src: Operand, base: Reg, offset: Operand) extends Instruction { + override def emit: String = s" strb ${src.toOperand}, [${base.toOperand}, ${offset.toOperand}]" +} + +// STUR +case class STUR(src: Operand, base: Reg) extends Instruction { + override def emit: String = s" stur ${src.toOperand}, [${base.toOperand}, #-4]" +} + +case class RET() extends Instruction { + override def emit: String = " ret" +} + +case class EOF() extends Instruction { + override def emit: String = "" +} + +// -------------------------- +// Bool flags +// -------------------------- + +sealed trait Flag extends Instruction +case object NE extends Flag { + override def emit: String = "ne" +} + +case object EQ extends Flag { + override def emit: String = "eq" +} + +case object GT extends Flag { + override def emit: String = "gt" +} + +case object LT extends Flag { + override def emit: String = "lt" +} + +case object GE extends Flag { + override def emit: String = "ge" +} + +case object LE extends Flag { + override def emit: String = "le" +} + +case object VS extends Flag { + override def emit: String = "vs" +} +case object FAILEDFLAG extends Flag { + override def emit: String = "failed" +} + +// ---------------------------------------------------- +// General Instruction Classes +// ---------------------------------------------------- + +// Contains all classes which perform the generic movement +// (To allow for all allocated types to work) + +/** + * A class which invokes the correct moving instruction for + * + */ +case class Move(dest: Allocated, src: Allocated) + (using allocator: RegisterAllocator) extends Instruction { + + // Emits the correct store instruction depending on the contents of src and dest + override def emit: String = getInstrs().map(_.emit).mkString("\n") + + // Functions pertaining instruction allocation + def getInstrs(): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + dest match { + // Check on the whether the operand is on the stack or a register + + // Using either MOV or LDR instructions + case regDest: Reg => src match { + // Move the Src operand to the Dest register + case opSrc: (Reg | Immediate) => instructions += MoveOps(regDest, opSrc) + + // Load the Src register to the Dest register + case Spill(offsetSrc, basePointer) => instructions += Load(regDest, basePointer, Some(offsetSrc)) + } + // Using STR instructions + case Spill(offsetDest, basePointer) => src match { + case regSrc: (Reg) => + instructions += Store(regSrc, basePointer, Some(offsetDest)) + + // TODO: Might need to add logic for tempReg Spill creation so that there's always a free + // temp alloc to move things to + // We will Load then store here + case Spill(offsetSrc, basePointer) => + // Create a temp x bit-mode register + val tempAlloc = allocator.allocateTemp(X_REG) + tempAlloc match { + case reg: Reg => + instructions += Load(reg, basePointer, Some(offsetDest)) + instructions += Store(reg, basePointer, Some(offsetSrc)) + } + allocator.freeTemp(tempAlloc) // Free it (location doesn't matter) + } + } + + instructions.toList + } +} + +/** + * A class which invokes the correct storing instruction + * + */ +case class Store(reg1: Reg, alloc2: Allocated, _imm3: Option[Immediate] = None) + (using allocator: RegisterAllocator) extends Instruction { + + // Emits the correct store instruction depending on the contents of src and dest + override def emit: String = getInstrs().map(_.emit).mkString("\n") + + def getInstrs(): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val regTemp = allocator.allocateTemp(X_REG) + _imm3 match { + case None => alloc2 match { + case reg2: Reg => instructions += STR(reg1, reg2) + case Spill(offset2, basePointer) => instructions += STR(reg1, basePointer, Some(offset2)) + } + case Some(imm3) => alloc2 match { + case reg2: Reg => imm3 match { + + // If the offset is less than -256, then we need to move the value to a register first + case Immediate(negVal) if negVal.toInt < -256 => + instructions += MOV(X17, imm3) + instructions += STR(reg1, reg2, Some(X17)) + + // If the offset is greater than 255, then we need to move the value to a register first + case Immediate(posVal) if posVal.toInt > 255 => + instructions += MoveOps(X17, imm3) + instructions += STR(reg1, reg2, Some(X17)) + case _ => + instructions += STR(reg1, reg2, Some(imm3)) + + } + case Spill(offset2, basePointer) => + instructions += Load(regTemp, basePointer, Some(offset2)) + instructions += STR(reg1, regTemp, Some(imm3)) + } + } + + allocator.freeTemp(regTemp) + instructions.toList + } +} + +/** + * A class which invokes the correct compare instruction + * + */ +case class Compare(alloc1: Allocated, op2: Operand, _sxtw: Option[SXTW] = None) + (using allocator: RegisterAllocator) extends Instruction { + override def emit: String = getInstrs().map(_.emit).mkString("\n") + + def getInstrs(): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + alloc1 match { + case reg: Reg => instructions += CMP(reg, op2, _sxtw) + case Spill(offset, basePointer) => + val regTemp = op2 match { + case reg: Reg if reg.bitMode == W_REG => allocator.allocateTemp(W_REG) + case _: Immediate => allocator.allocateTemp(W_REG) + case _ => allocator.allocateTemp(X_REG) + } + + instructions += LDR(regTemp, basePointer, Some(offset)) + instructions += CMP(regTemp, op2, _sxtw) + allocator.freeTemp(regTemp) + } + instructions.toList + } +} + +/** + * A class which invokes the correct load instruction + * + */ +case class Load(dest: Reg, base: Allocated, offset: Option[Operand] = None, shift: Option[Shift] = None) + (using allocator: RegisterAllocator) extends Instruction { + override def emit: String = getInstrs().map(_.emit).mkString("\n") + + def getInstrs(): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + base match { + case reg: Reg => + offset match { + + // If the offset is less than -256, then we need to move the value to a register first + case Some(negImm@Immediate(negVal)) if negVal.toInt < -256 => + instructions += MOV(X17, negImm) + instructions += LDR(dest, reg, Some(X17), shift) + + // If the offset is greater than 255, then we need to move the value to a register first + case Some(posImm@Immediate(posVal)) if posVal.toInt > 255 => + instructions += MoveOps(X17, posImm) + instructions += LDR(dest, reg, Some(X17), shift) + + // Otherwise, just load the values as is + case _ => instructions += LDR(dest, reg, offset, shift) + + } + + case Spill(offsetBase, basePointer) => + + offset match { + + // We load twice if the offset exists + case Some(_) => + val tempReg = allocator.allocateTemp(X_REG) + instructions += LDR(tempReg, basePointer, Some(offsetBase)) + instructions += LDR(dest, tempReg, offset, shift) + allocator.freeTemp(tempReg) + + // We load straight to the destination + case None => instructions += LDR(dest, basePointer, Some(offsetBase)) + + } + } + + instructions.toList + } +} \ No newline at end of file diff --git a/src/main/wacc/backend/labelGen.scala b/src/main/wacc/backend/labelGen.scala new file mode 100644 index 0000000..7d7eba0 --- /dev/null +++ b/src/main/wacc/backend/labelGen.scala @@ -0,0 +1,399 @@ +package wacc.backend + +import scala.collection.mutable + +// All libc definitions +val scanf = "scanf" +val exit = "exit" +val free = "free" +val puts = "puts" +val printf = "printf" +val fflush = "fflush" +val malloc = "malloc" + +// All Standard Label definitions +val _prints = "_prints" +val _printc = "_printc" +val _printi = "_printi" +val _printb = "_printb" +val _printp = "_printp" +val _println = "_println" +val _readi = "_readi" +val _readc = "_readc" +val _malloc = "_malloc" +val _errOutOfMemory = "_errOutOfMemory" +val _errOutOfBounds = "_errOutOfBounds" +val _errBadChar = "_errBadChar" +val _errOverflow = "_errOverflow" +val _errDivZero = "_errDivZero" +val _errNull = "_errNull" +def _arrLoad(size: String) = s"_arrLoad$size" +def _arrStore(size: String) = s"_arrStore$size" +val _freepair = "_freepair" + +/** + * LabelGenerator provides unique labels used for branch targets + * We use a simple counter that is incremented on every request + * Also produces standard labels (for read, print, and any other call that may be needed) + */ +class LabelGenerator { + private var branchCounter = -1 + private var stringCounter = -1 + + private def prologue(label: String, reg1: Reg, reg2: Reg): List[Instruction] = List ( + Label(label), + Comment(s"push {${reg1.toOperand}${if (reg2 != XZR) s", ${reg2.toOperand}" else ""}}"), + STP(reg1, reg2, SP) + ) + + private def epilogue(reg1: Reg, reg2: Reg): List[Instruction] = List ( + Comment(s"pop {${reg1.toOperand}${if (reg2 != XZR) s", ${reg2.toOperand}" else ""}}"), + LDP(reg1, reg2, SP), + RET() + ) + + // Maps each label name to its full instruction set + private val widgetMap: Map[String, List[Instruction]] = Map( + _prints -> printInstr('s'), _printc -> printInstr('c'), _printi -> printInstr('i'), + _printb -> printInstr('b'), _printp -> printInstr('p'), _println -> printInstr('l'), + _readi -> readInstr('i'), _readc -> readInstr('c'), _malloc -> mallocInstr(), + _errOutOfMemory -> errInstr('m'), _errOutOfBounds -> errInstr('b'), + _errBadChar -> errInstr('c'), _errOverflow -> errInstr('o'), _errDivZero -> errInstr('z'), + _errNull -> errInstr('n'), _arrLoad("1") -> arrNInstr("Load", '1'), + _arrLoad("4") -> arrNInstr("Load", '4'), _arrLoad("8") -> arrNInstr("Load", '8'), + _arrStore("1") -> arrNInstr("Store", '1'), _arrStore("4") -> arrNInstr("Store", '4'), + _arrStore("8") -> arrNInstr("Store", '8'), _freepair -> freePairInstr() + ) + + // Contains the unique standard labels that have been called through the program + private val widgetSet: mutable.Set[String] = mutable.Set.empty + + // A map to hold all string literals (so that duplicates reuse the same label) + private val stringLiterals: mutable.Map[String, (String, String)] = mutable.Map.empty + + def getStringLiterals(): List[(String, (String, String))] = stringLiterals.toSeq.sortBy { + // Sort by the number of the string label + _._2._2.stripPrefix(".L.str").toInt + }.toList + + /** + * Escapes special characters in a string literal + */ + def escapeString(s: String): String = { + s.flatMap { + case '\n' => "\\n" + case '\t' => "\\t" + case '\r' => "\\r" + case '\"' => "\\\"" + case '\\' => "\\\\" + case c => c.toString + } + } + + /** + * Generates all of the standard labels, this depends on how populated the set is + */ + def genWidgets(): List[Instruction] = { + widgetSet.toSeq.sortBy(str => str).toList.flatMap(EOF() +: widgetMap.get(_).get) + } + + def addWidget(label: String): Unit = { + if (widgetMap.contains(label)) { + widgetSet += label + label match { + // Add the errOutOfMemory and prints labels for a malloc + case s"$mallocLabel" if mallocLabel == _malloc => + widgetSet += _errOutOfMemory + widgetSet += _prints + + // Add the errNull and prints labels for a freepair + case s"$freepairLabel" if freepairLabel == _freepair => + widgetSet += _errNull + widgetSet += _prints + + // For overflow, div by zer, or null errors, add the prints label + case s"$errLabel" if errLabel == _errOverflow || errLabel == _errDivZero || errLabel == _errNull => + widgetSet += _prints + + // Matches on the 3 arrLoad/arrStore standard labels and adds the _errOutOfBounds label to the set + case label if (label.startsWith(_arrLoad("")) || label.startsWith(_arrStore(""))) => + widgetSet += _errOutOfBounds + + case _ => () + } + } + } + + def freshLabel(str: String): String = { + // If the map doesn't contain the string, then generate a new label + if (!stringLiterals.contains(str)) { + stringCounter += 1 + stringLiterals += (str -> (escapeString(str), s".L.str$stringCounter")) + } + + // Return the label + stringLiterals.get(str).get._2 + } + + /** + * Generates a fresh label for a branching instruction (while/if-else) + * Returns the label string + */ + def freshLabel(): String = { + branchCounter += 1 + s".L${branchCounter}" + } + + + // ---------------------------------------------------- + // Standard labels + // ---------------------------------------------------- + + /** + * Formats print label instructions for chars, strings, ints, pointers, booleans, and println + */ + def printInstr(_type: Char): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val typeToEsc = Map('p' -> "%p", 'c' -> "%c", 'i' -> "%d") + val typePrinted = if (_type == 'l') "ln" else _type + + instructions += Comment(s"length of .L._print${typePrinted}_str0", "") + // Add the meta data for the correct print type + _type match { + case chr @ ('p' | 'c' | 'i') => + instructions ++= List( + Directive("word 2", " "), + Label(s".L._print${chr}_str0"), + Directive(s"asciz \"${typeToEsc.get(chr).get}\"", " ") + ) + case 's' => + instructions ++= List( + Directive("word 4", " "), + Label(s".L._prints_str0"), + Directive(s"asciz \"%.*s\"", " ") + ) + case 'b' => + instructions ++= List( + Directive("word 5", " "), + Label(s".L._printb_str0"), + Directive(s"asciz \"false\"", " "), + Comment(s"length of .L._printb_str1", ""), + Directive("word 4", " "), + Label(s".L._printb_str1"), + Directive(s"asciz \"true\"", " "), + Comment(s"length of .L._printb_str2", ""), + Directive("word 4", " "), + Label(s".L._printb_str2"), + Directive(s"asciz \"%.*s\"", " ") + ) + + case 'l' => + instructions ++= List( + Directive("word 0", " "), + Label(s".L._println_str0"), + Directive(s"asciz \"\"", " ") + ) + + case _ => () + } + + // Common preamble for all print instructions + instructions += Directive("align 4") + instructions ++= prologue(s"_print${typePrinted}", LR, XZR) + + // Generate the address manipulation depending on the type of print + _type match { + case 'p' | 'c' | 'i' => + instructions += MoveOps(X1, X0) + + case 's' => + instructions += MoveOps(X2, X0) + instructions += LDUR(W1, X0) + + case 'b' => + instructions ++= List( + CMP(W0, Imm0), + new BCond(NE, ".L_printb0"), + ADR(X2, s".L._printb_str0"), + B(".L_printb1"), + Label(".L_printb0"), + ADR(X2, s".L._printb_str1"), + Label(".L_printb1"), + LDUR(W1, X2) + ) + + case _ => () + } + + // Generate the remaining routine depending on the type of print + if (_type == 'b') { + instructions += ADR(X0, s".L._printb_str2") + } else { + instructions += ADR(X0, s".L._print${typePrinted}_str0") + } + if (_type == 'l') { + instructions += new BL(puts) + } else { + instructions += new BL(printf) + } + + instructions += MoveOps(X0, Imm0) + instructions += new BL(fflush) + instructions ++= epilogue(LR, XZR) + + instructions.toList + } + + /** + * Formats a standard read label instruction for chars, strings, ints, pointers, and booleans + */ + def readInstr(_type: Char): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val label = s".L._read${_type}_str0" + + instructions += Comment(s"length of $label", "") + + _type match { + case 'i' => + instructions ++= List( + Directive("word 2", " "), + Label(label), + Directive(s"asciz \"%d\"", " ") + ) + + case 'c' => + instructions ++= List( + Directive("word 3", " "), + Label(label), + Directive(s"asciz \" %c\"", " ") + ) + + case _ => () + } + + instructions += Directive("align 4") + instructions ++= prologue(label.replace(".L.", "").replace("_str0", ""), X0, LR) + instructions ++= List( + MoveOps(X1, SP), + ADR(X0, label), + new BL(scanf), + ) + + instructions ++= epilogue(X0, LR) + + instructions.toList + } + + def mallocInstr(): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + instructions ++= prologue(_malloc, LR, XZR) + instructions ++= List( + new BL(malloc), + CBZ(X0, _errOutOfMemory), + ) + + instructions ++= epilogue(LR, XZR) + + instructions.toList + } + + def errInstr(errType: Char): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Create variables for the name and word length for the type + val (errName, errWord, errMsg) = errType match { + case 'm' => (_errOutOfMemory, "word 27", "out of memory") + case 'b' => (_errOutOfBounds, "word 42", "array index %d out of bounds") + case 'c' => (_errBadChar, "word 50", "int %d is not ascii character 0-127") + case 'o' => (_errOverflow, "word 52", "integer overflow or underflow occurred") + case 'z' => (_errDivZero, "word 40", "division or modulo by zero") + case 'n' => (_errNull, "word 45", "null pair dereferenced or freed") + case _ => ("", "", "") + } + + // Add the preamble for instructions + instructions ++= List( + Comment(s"length of .L.${errName}_str0", ""), + Directive(errWord, " "), + Label(s".L.${errName}_str0"), + Directive(s"asciz \"fatal error: ${errMsg}\\n\"", " "), + Directive("align 4"), + Label(errName), + ADR(X0, s".L.${errName}_str0") + ) + + val remainingInstr = errType match { + case ('m' | 'o' | 'z' | 'n') => List( + new BL(_prints) + ) + case ('b' | 'c') => List( + new BL(printf), + MoveOps(X0, Imm0), + new BL(fflush) + ) + case _ => List() + } + + instructions ++= remainingInstr + + // Add on the final common part for all error labels + instructions ++= List( + MoveOps(W0, Imm_1), + new BL(exit) + ) + + instructions.toList + } + + def arrNInstr(_type: String, n :Char): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + _type match { + case "Store" => Comment(s"Special calling convention: array ptr passed in X7, index in X17," + + " value to store in X8, LR(W30) is used as general register") + case "Load" => Comment("Special calling convention: array ptr passed in X7, index in X17," + + " LR(W30) is used as general register, and return into X7") + } + instructions ++= prologue(s"_arr$_type$n", LR, XZR) + instructions ++= List( + CMP(W17, Imm0), + Comment("this must be a 64-bit move so that it doesn't truncate if the move fails"), + CSEL(X1, X17, X1, LT), + new BCond(LT, _errOutOfBounds), + LDUR(W30, X7), + CMP(W17, W30), + Comment("this must be a 64-bit move so that it doesn't truncate if the move fails"), + CSEL(X1, X17, X1, GE), + new BCond(GE, _errOutOfBounds) + ) + + _type match { + case "Load" => n match { + case '8' => instructions += LDR(X7, X7, Some(X17), Some(LSL(Imm3))) + case '4' => instructions += LDR(W7, X7, Some(X17), Some(LSL(Imm2))) + case '1' => instructions += LDRB(W7, X7, X17) + case _ => () + } + case "Store" => n match { + case '8' => instructions += STR(X8, X7, Some(X17), Some(LSL(Imm3))) + case '4' => instructions += STR(W8, X7, Some(X17), Some(LSL(Imm2))) + case '1' => instructions += STRB(W8, X7, X17) + case _ => () + } + } + + instructions ++= epilogue(LR, XZR) + instructions.toList + + } + + def freePairInstr(): List[Instruction] = { + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + instructions ++= prologue(_freepair, LR, XZR) + instructions += CBZ(X0, _errNull) + instructions += new BL(free) + instructions ++= epilogue(LR, XZR) + + instructions.toList + } +} \ No newline at end of file diff --git a/src/main/wacc/backend/lhsRhsGen.scala b/src/main/wacc/backend/lhsRhsGen.scala new file mode 100644 index 0000000..5fcc451 --- /dev/null +++ b/src/main/wacc/backend/lhsRhsGen.scala @@ -0,0 +1,311 @@ +package wacc.backend +import wacc.frontend.syntax.ast._ +import wacc.frontend.semantic.environment._ +import scala.collection.mutable + +/** + * Generates assembly for a RHS + */ +def genRhs(rhs: Rhs, cGen: CodeGenerator) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator, + gFuncEnv: GlobalFuncEnv, funcRegList: List[(String, Allocated)] = List.empty): + (List[Instruction], Reg) = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + rhs match { + case expr: Expr => genExpr(expr) + + case NewPair(_, fst, snd) => + + // Save argument registers + instructions ++= cGen.generatePushInstructions(funcRegList, callingFunc = true) + + instructions ++= List( + MoveOps(W0, Imm16), + BL(_malloc), + MoveOps(X16, X0) + ) + + // Pop off argument registers + instructions ++= cGen.generatePopInstructions(funcRegList.reverse.toList, callingFunc = true) + + val (fstInstr, fstAlloc) = genExpr(fst) + val xFst = PhysicalReg(fstAlloc.regNumber, X_REG, fstAlloc.pool) + instructions ++= fstInstr + instructions += STR(xFst, X16) + allocator.freeTemp(fstAlloc) + + val (sndInstr, sndAlloc) = genExpr(snd) + val xSnd = PhysicalReg(sndAlloc.regNumber, X_REG, sndAlloc.pool) + instructions ++= sndInstr + instructions += STR(xSnd, X16, Some(Imm8)) + allocator.freeTemp(sndAlloc) + + val pairAlloc = allocator.allocateTemp(X_REG) + instructions += MoveOps(pairAlloc, X16) + (instructions.toList, pairAlloc) + + case arr@ArrayLiter(_, elems) => + + // Save argument registers + instructions ++= cGen.generatePushInstructions(funcRegList, callingFunc = true) + + // Allocate the first temp + val tempAlloc1 = allocator.allocateTemp(W_REG) + + // Create the immediate that is to be used for mallocing to the result register + val typeSize = sizeof(exprType(elems.headOption.getOrElse(PairLiter(IGNORE)))(using gMainEnv)) + val arrImm = Immediate((typeSize * elems.length) + 4) + val elemsImm = Immediate(elems.length) + + // Add the preamble for adding elements to the array + instructions ++= List( + Comment(s"${elems.length} element array"), + MoveOps(W0, arrImm), + BL(_malloc), + MoveOps(X16, X0) + ) + + // Pop off argument registers + instructions ++= cGen.generatePopInstructions(funcRegList.reverse.toList, callingFunc = true) + + instructions ++= List( + Comment("array pointers are shifted forwards by 4 bytes (to account for size)"), + ADD(X16, X16, Imm4), + MoveOps(tempAlloc1, elemsImm), + STUR(tempAlloc1, X16) + ) + allocator.freeTemp(tempAlloc1) + + // Add each element to the memory location (depending on the type of the element) + instructions ++= elems.zipWithIndex.flatMap{(elem: Expr, index: Int) => + + // Allocate the temp allocation for both bit-modes as they are both needed + val (exprInstr, exprAlloc) = genExpr(elem) + val (exprAllocX, exprAllocW) = exprAlloc match { + case xReg@PhysicalReg(regNumber, bitMode, pool) => + (xReg, PhysicalReg(regNumber, W_REG, pool)) + } + + // Add the specific element type + val indexImm = Immediate(index * typeSize) + val specificInstr = exprType(elem)(using gMainEnv) match { + case IntType => List( + STR(exprAllocW, X16, Some(indexImm)) + ) + case (CharType | BoolType) => List( + STRB(exprAllocW, X16, indexImm) + ) + case _ => List( + STR(exprAllocX, X16, Some(indexImm)) + ) + } + + allocator.freeTemp(exprAlloc) + exprInstr ++ specificInstr + } + + // The final array is always in an x bit-mode register + val tempAlloc = allocator.allocateTemp(X_REG) + instructions += MoveOps(tempAlloc, X16) + + (instructions.toList, tempAlloc) + + case Call(_, id, args) => + instructions += Comment("FUNCTION CALL") + + // Store parameter args onto stack before moving values + instructions ++= cGen.generatePushInstructions(funcRegList, callingFunc = true) + + // Add the needed stack space for extra arguments (args 9 onwards) + val _addedStackSpace = ((args.size - MAX_PARAM_REGS + 1) & ~1) * EIGHT_BYTES + val addedStackSpace = Immediate(_addedStackSpace) + if (_addedStackSpace > 0) { + instructions += SUB(SP, SP, addedStackSpace) + } + + // Efficiently retrieves and argument and then moves the content to the correct arg register + instructions ++= args.zipWithIndex.flatMap{ case (arg, index) => + val instrs: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + + // Get the instructions and allocation for the argument + val (argInstr, argAlloc) = genExpr(arg) + + // Create an allocation for a parameter, this will either be a register or memory on the stack + val paramAlloc = index match { + case paramReg if paramReg < MAX_PARAM_REGS => Register(s"$index", argAlloc.bitMode) + case paramSpill => + val _offset = (paramSpill - MAX_PARAM_REGS) * EIGHT_BYTES + val offset = Immediate(_offset) + Spill(offset, SP) + } + + // Add the argument's instructions and then move the argument's temp to the corresponding parameter reg + // Restoration of registers occurs in the declaration and assignment statements + instrs ++= argInstr + instrs += Move(paramAlloc, argAlloc) + allocator.freeTemp(argAlloc) + + instrs.toList + } + + // Call the function + + // The id of the function used to query the global function environment + val funcKey = id.id + + // Replace # with an underscore for funcLabel + val funcLabel = id.id.replaceAll("#", "_") + instructions += BL(funcLabel) + + // Offload all stack manipulation for extra arguments + if (_addedStackSpace > 0) { + instructions += ADD(SP, SP, addedStackSpace) + } + + // Get lhs type + val lhsType = gFuncEnv.lookup(funcKey).getOrElse( + throw new RuntimeException(s"Function $funcKey not found in global function environment, couldnt get lhs") + )._type + + val returnReg = lhsType match { + case (IntType | CharType | BoolType) => W0 + case _ => X0 + } + + // Registers will be popped after declaration / assignment to avoid overwriting X0 + (instructions.toList, returnReg) + } +} + +/** + * Generates assembly for a LHS + */ +def genLhs(lhs: Lhs) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator, funcRegList: List[(String, Allocated)] = List.empty): + (List[Instruction], Allocated) = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + lhs match { + // Differs to the expr identifier parsing as it just releases the result + // Needed to distinguish between assignments and just using the variable + case Ident(_, id) => + val idAlloc = allocator.getAllocMapping(id) + (instructions.toList, idAlloc) + case PairElem(_, selector, expr) => + val (exprInstr, exprAlloc) = genLhs(expr) + instructions += Compare(exprAlloc, Imm0) + instructions += BCond(EQ, _errNull) + instructions ++= exprInstr + (instructions.toList, exprAlloc) + + // All other LHSs call genExpr and special cases are handled in the calling statement + case expr: Expr => genExpr(expr) + } +} + +/** + * Generates assembly for a LHS considering special cases for Pair and Array Elems(when storing) + */ +def genLhsForUsing(lhs: Lhs, parentInstrs: mutable.ListBuffer[Instruction]) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + (List[Instruction], Allocated) = { + + lhs match { + // In the case of an ArrayElem, you take the first n-1 indices + // where n is the length of the indices list + case ArrayElem(pos, id, indices) => + val shortenedIndices = indices.take(indices.length - 1) + val (instr, tempAlloc) = genLhs(ArrayElem(pos, id, shortenedIndices)) + + // If the shortened query is nothing, then move the identifier's address + // to the address that's been generated + if (shortenedIndices.isEmpty) { + val idAlloc = allocator.getAllocMapping(id.id) + parentInstrs += Move(tempAlloc, idAlloc) + } + + // Return the instructions and allocation generated + // instr is empty in the case of indicies being an empty list + (instr, tempAlloc) + + // In the case of a PairElem, you need to unfold the PairElem to get the list of selectors + // and then iteratively load the values from the pair in reverse order + case _: PairElem => + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + val (selectorList, finalIdent) = unfoldPairElem(lhs) + val (lhsInstr, lhsAlloc) = genLhs(finalIdent) + var tempAlloc = lhsAlloc + val reverse = selectorList.drop(1).reverse // Top case is handled in genStmt Assign + + if (reverse.isEmpty) { + instructions += Compare(tempAlloc, Imm0) + instructions += BCond(EQ, _errNull) + } // We'll need to remove duplication of code between here & genLhs + + instructions ++= lhsInstr + reverse.foreach { selector => + val tempAlloc2 = allocator.allocateTemp(X_REG) + val offset = selector match { + case Fst => Some(Imm0) + case Snd => Some(Imm8) + } + instructions += Load(tempAlloc2, tempAlloc, offset) + allocator.freeTemp(tempAlloc) + instructions += Compare(tempAlloc, Imm0) + instructions += BCond(EQ, _errNull) + tempAlloc = tempAlloc2 + } + (instructions.toList, tempAlloc) + + + case other => genLhs(lhs) + } +} + +/* + * Unfolds a PairElem to get the list of selectors and the final LHS +*/ +def unfoldPairElem(lhs: Lhs): (List[PairSelector], Lhs) = lhs match { + case PairElem(_, selector, innerLhs) => + val (selectors, finalLhs) = unfoldPairElem(innerLhs) + (selector :: selectors, finalLhs) + case _ => (Nil, lhs) +} + +/** + * Performs a move of the rhs to the lhs depending on the type of LHS + */ +def lhsMove(lhs: Lhs, lhsAlloc: Allocated, rhsAlloc: Reg, reg8: Reg) + (using gMainEnv: GlobalMainEnv, allocator: RegisterAllocator, labelGenerator: LabelGenerator): + List[Instruction] = { + + val instructions: mutable.ListBuffer[Instruction] = mutable.ListBuffer.empty + lhs match { + // Call on _arrayStoreN function where N is the size of the type + case arrElem@ArrayElem(_, id, indices) => + + // Retrieve the final indexes instruction and allocation + val (finalIndexInstr, finalIndexAlloc) = genExpr(indices.last) + instructions ++= finalIndexInstr + instructions ++= List( + MoveOps(W17, finalIndexAlloc), + MoveOps(reg8, rhsAlloc), + Move(X7, lhsAlloc), + BL(_arrStore(s"${sizeof(lhsType(arrElem))}")) + ) + + allocator.freeTemp(finalIndexAlloc) + + case PairElem(_, selector, expr) => + selector match { + case Fst => instructions += Store(rhsAlloc, lhsAlloc) + case Snd => instructions += Store(rhsAlloc, lhsAlloc, Some(Imm8)) + } + + // Otherwise, move the contents of temp allocation to identifier allocation + case _ => instructions += Move(lhsAlloc, rhsAlloc) + } + + instructions.toList +} \ No newline at end of file diff --git a/src/main/wacc/backend/registerAllocator.scala b/src/main/wacc/backend/registerAllocator.scala new file mode 100644 index 0000000..3d861f6 --- /dev/null +++ b/src/main/wacc/backend/registerAllocator.scala @@ -0,0 +1,288 @@ +package wacc.backend + +import scala.collection.mutable + +// ---------------------------------------------------- +// Constants +// ---------------------------------------------------- + +// Constant for the max number of variables that can be stored in a callee reg +val MAX_CALLEE_REGS = CalleePool.available.length +val MAX_PARAM_REGS = 8 + +// Byte constants +val SIXTEEN_BYTES = 16 +val EIGHT_BYTES = 8 + +val X_REG = 'x' +val W_REG = 'w' +val FP_REG = "fp" +val SP_REG = "sp" +val LR_REG = "lr" +val XZR_REG = "xzr" + +// Registers that are used through the program (particularly for standard labels) +val X0 = Register("0", X_REG) +val W0 = Register("0", W_REG) +val X1 = Register("1", X_REG) +val W1 = Register("1", W_REG) +val X2 = Register("2", X_REG) +val X7 = Register("7", X_REG) +val W7 = Register("7", W_REG) +val X8 = Register("8", X_REG) +val W8 = Register("8", W_REG) +val X16 = Register("16", X_REG) +val W16 = Register("16", W_REG) +val X17 = Register("17", X_REG) +val W17 = Register("17", W_REG) + +// Special registers +val W30 = Register("30", W_REG) +val FP = Register(FP_REG, ' ') +val SP = Register(SP_REG, ' ') +val LR = Register(LR_REG, ' ') +val XZR = Register(XZR_REG, ' ') + +// Immediate values +val Imm_1 = Immediate(-1) +val Imm0 = Immediate(0) +val Imm1 = Immediate(1) +val Imm2 = Immediate(2) +val Imm3 = Immediate(3) +val Imm4 = Immediate(4) +val Imm8 = Immediate(8) +val Imm16 = Immediate(16) + + +// Represents an allocated value for code generation +sealed trait Allocated { + def toOperand: String +} + +// Representds an operand value for parsing into instructions (Can only be registers or immediate values) +sealed trait Operand { + def toOperand: String +} + +// Label ops are for strings with the :lo12: prepended to the label +case class LabelOp(strLabel: String) extends Operand { + override def toOperand: String = s":lo12:$strLabel" +} + +// Represents a physical register allocation +sealed trait Reg extends Allocated with Operand { + + // Every register should have a register number and a bitmode + def regNumber: String + def bitMode: Char + + override def toOperand: String = bitMode match { + case space if space == ' ' => s"${regNumber}" + case _ => s"${bitMode}${regNumber}" + } +} + +object Reg { + + // Contains the set of allowed bitmodes and register number that a reg can have + val allowedBitModes = Set(X_REG, W_REG, ' ') + val allowedRegNums = (0 to 31).map(_.toString()).toSet ++ Set(FP_REG, SP_REG, LR_REG, XZR_REG) + + // Each application of a type of register must have the allowed parameters otherwise they can't be created + def validate(regNumber: String, bitMode: Char): Unit = { + if (!allowedRegNums.contains(regNumber) || !allowedBitModes.contains(bitMode)) { + throw IllegalArgumentException("Cannot have a register of such number/bitmode") + } + } +} + +// A concrete physical register allocated from a specific pool +case class PhysicalReg(regNumber: String, bitMode: Char, pool: RegisterPool) extends Reg { + Reg.validate(regNumber, bitMode) +} +case class Register(regNumber: String, bitMode: Char) extends Reg { + Reg.validate(regNumber, bitMode) +} + +// A class representing an immediate value +case class Immediate(value: String) extends Operand { + override def toOperand: String = s"#$value" +} +object Immediate { + def apply(value: String): Immediate = new Immediate(value) + def apply(value: BigInt): Immediate = new Immediate(s"$value") +} + +// A spill slot allocation on the stack +// (Can change the basePointer if we want something other than FP) +case class Spill(offset: Immediate, basePointer: Reg = FP) extends Allocated { + override def toOperand: String = { + s"[${basePointer.toOperand}, ${offset.toOperand}]" + } +} + +// A trait to tag register pools +sealed trait RegisterPool { + def available: List[String] + def name: String +} + +// The callee-saved register pool (register numbers as strings) +case object CalleePool extends RegisterPool { + override val available: List[String] = + List("19", "20", "21", "22", "23", "24", "25", "26", "27", "28") + override val name: String = "callee" +} + +// The temporary register pool (register numbers as strings) +case object TempPool extends RegisterPool { + override val available: List[String] = + List("8", "9", "10", "11", "12", "13", "14", "15") + override val name: String = "temp" +} + +/** + * + * Allocates either Physical registers (x or w bit mode registers) + * It maintains two independent pools: + * one for callee registers and one for temporary registers. + * Allocation functions take a parameter (W_REG or X_REG) to determine the correct register type + */ +class RegisterAllocator { + private var calleeFreeRegNums: List[String] = CalleePool.available + private var tempFreeRegNums: List[String] = TempPool.available + + // Map from an identifier to its allocated value + val identifierMap = mutable.Map[String, Allocated]() + + // A counter to allocate unique spill slots (each 8 bytes) + // Default is the negative of the max num of callee registers (As we do things in reference to fp) + private var spillCounter: Int = -1 * MAX_CALLEE_REGS + + private def allocateSpillSlot(): Immediate = { + spillCounter -= 1 + Immediate(spillCounter * EIGHT_BYTES) + } + + /** + * Adds an allocation mapping for an identifier + * The mapping preserves the exact register type (including its bit mode) + */ + def addAllocMapping(ident: String, reg: Allocated): Unit = { + identifierMap += (ident -> reg) + } + + /** + * Retrieves the allocated value for an identifier + */ + def getAllocMapping(ident: String): Allocated = + identifierMap.getOrElse(ident, throw new RuntimeException(s"Identifier $ident not allocated")) + + /** + * Gets the number of variables used in the program //TODO: May have to change this logic a bit later + */ + def getNumVariables(): BigInt = identifierMap.size + + /** + * Allocates a temporary register with the given bit mode (W_REG or X_REG) + * Assumption: There is always an available temp register + */ + def allocateTemp(bitMode: Char): PhysicalReg = + tempFreeRegNums match { + case regNum :: rest => + tempFreeRegNums = rest + PhysicalReg(regNum, bitMode, TempPool) + // Just a fall back which shouldn't happen according to our policy + case Nil => PhysicalReg("Temp Pool should be available", ' ', TempPool) + } + + /** + * Allocates a callee register with the given bit mode (W_REG or X_REG) + * If no registers remain, a spill slot is allocated + */ + def allocateCallee(bitMode: Char): Allocated = + calleeFreeRegNums match { + case regNum :: rest => + calleeFreeRegNums = rest + PhysicalReg(regNum, bitMode, CalleePool) + case Nil => + Spill(allocateSpillSlot()) + } + + /** + * Frees a previously allocated temp register + * Callee registers cannot be freed + */ + def freeTemp(alloc: Allocated): Unit = alloc match { + case reg: PhysicalReg => reg.pool match { + case TempPool => + if (!tempFreeRegNums.contains(reg.regNumber)) + tempFreeRegNums = reg.regNumber :: tempFreeRegNums + + // Can only free a temp reg + case CalleePool => () + } + case _ => () + } + + /** + * Restores a temp if the pool isn't full + */ + def restore(): Unit = { + if (tempFreeRegNums.length < TempPool.available.length) { + // This is possible due to policy of Temp pool always being available + val restoredRegNum = tempFreeRegNums.headOption.getOrElse("16").toInt - 1 + restoredRegNum match { + case _ if restoredRegNum >= 8 => tempFreeRegNums = s"$restoredRegNum" :: tempFreeRegNums + case _ => () + } + + } + } + + /** + * Frees a previously allocated callee + * For physical registers, the register number is returned to the corresponding pool + * Spill slots are not reclaimed + * Temp registers cannot be freed + */ + def freeCallee(alloc: Allocated): Unit = alloc match { + case reg: PhysicalReg => + reg.pool match { + // Can only free a callee reg + case TempPool => () + case CalleePool => + if (!calleeFreeRegNums.contains(reg.regNumber)) + calleeFreeRegNums = reg.regNumber :: calleeFreeRegNums + } + case _ => // Other allocations are not reclaimed + } + + /** + * Resets the allocator: returns all registers to their pools + * resets the spill counter, and clears identifier mappings + */ + def reset(): Unit = { + calleeFreeRegNums = CalleePool.available + tempFreeRegNums = TempPool.available + } + + /** + * Saves all of the register information from before to later be restored + */ + def saveState(): (List[String], List[String], Int, mutable.Map[String, Allocated]) = { + (calleeFreeRegNums, tempFreeRegNums, spillCounter, identifierMap.clone()) + } + + /** + * Restores the state of the allocator + */ + def restoreState(state: (List[String], List[String], Int, mutable.Map[String, Allocated])): Unit = { + val (savedCalleeFreeRegNums, savedTempFreeRegNums, savedSpillCounter, savedIdentifierMap) = state + calleeFreeRegNums = savedCalleeFreeRegNums + tempFreeRegNums = savedTempFreeRegNums + spillCounter = savedSpillCounter + identifierMap.clear() + identifierMap ++= savedIdentifierMap + } +} \ No newline at end of file diff --git a/src/main/wacc/backend/typeAnalysis.scala b/src/main/wacc/backend/typeAnalysis.scala new file mode 100644 index 0000000..55a0eae --- /dev/null +++ b/src/main/wacc/backend/typeAnalysis.scala @@ -0,0 +1,75 @@ +package wacc.backend + +import wacc.frontend.syntax.ast._ +import wacc.frontend.semantic.environment._ + +val IGNORE = (-1, -1) + +/** + * Allocates the size of a type + */ +def sizeof(_type: Type) = _type match { + case IntType => 4 + case (CharType | BoolType) => 1 + case _ => 8 +} + +/** + * Finds the type of an expression + */ +def exprType(expr: Expr)(using gMainEnv: EnvTrait): Type = + expr match { + case _: (IntLiter | Neg | Len | Ord | Mul | Div | Mod | Add | Sub) => IntType + case _: (BoolLiter | Not | Gt | Geq | Lt | Leq | Eq | Neq | And | Or) => BoolType + case _: (CharLiter | Chr) => CharType + case _: StrLiter => StrType + case _: PairLiter => PairPlaceholder // The null type is a PairPlaceholder + case Ident(_, id) => + gMainEnv.lookupType(id).getOrElse(AnyType) + case ArrayElem(_, id, indices) => + // Peel off array layers from the type of the id + val arrType = exprType(id) + val finalType = { + indices.foldLeft(arrType) { + case (ArrayType(inner), _) => inner + case (_, _) => AnyType + } + } + finalType + case pairElem: PairElem => lhsType(pairElem) + case IfExpr(pos, cond, thenBranch, elseBranch) => exprType(thenBranch) +} + +/** + * Finds the type of a RHS + */ +def rhsType(rhs: Rhs) + (using gMainEnv: EnvTrait, gFuncEnv: EnvTrait): Type = rhs match { + case expr: Expr => exprType(expr)(using gMainEnv) + case ArrayLiter(_, elems) => + // An array may have at least 1 element where its type is found through the call to exprType + ArrayType(exprType(elems.headOption.getOrElse(PairLiter(IGNORE)))(using gMainEnv)) + case NewPair(_, fst, snd) => (exprType(fst)(using gMainEnv), exprType(snd)(using gMainEnv)) match { + case (fType, sType) => PairType(fType, sType) + } + case Call(_, ident, args) => + gFuncEnv.lookup(ident.id).get._type +} + +/** + * Finds the type of a LHS + */ +def lhsType(lhs: Lhs) + (using gMainEnv: EnvTrait): Type = lhs match { + case identifier: (Ident | ArrayElem) => exprType(identifier) + case PairElem(_, selector, subLhs) => + val subType = lhsType(subLhs) + (selector, subType) match { + case (Fst, PairType(PairPlaceholder, _)) => PairPlaceholder + case (Snd, PairType(_, PairPlaceholder)) => PairPlaceholder + case (Fst, PairType(t:Type, _)) => t + case (Snd, PairType(_, t:Type)) => t + // This is for any null case, so would be named an AnyType may have TODO a change on this + case _ => AnyType + } +} \ No newline at end of file diff --git a/src/main/wacc/extension/peephole.scala b/src/main/wacc/extension/peephole.scala new file mode 100644 index 0000000..490fd7d --- /dev/null +++ b/src/main/wacc/extension/peephole.scala @@ -0,0 +1,267 @@ +package wacc.extension + +import wacc.backend._ +import scala.collection.mutable + +class Peephole { + def optimize(instructions: List[Instruction]): List[Instruction] = { + Function.chain(List(removeRedundantLoadandStores, removeRedundantMoves))(instructions) + } + + private def removeRedundantLoadandStores(instructions: List[Instruction]): List[Instruction] = { + val substitutions = mutable.ListBuffer[(Allocated | Operand, Reg, Int)]() + val removalList = mutable.ListBuffer[Int]() + + instructions.zipWithIndex.foreach({ + // Keep track of stored values, and index of instruction, and remove in situations where the register may be changed + case (STP(dest, src, base, _), index) => + substitutions += ((dest, base, index)) + substitutions += ((src, base, index)) + + case (STR(dest, base, _, _), index) => substitutions += ((dest, base, index)) + case (STRB(dest, base, _), index) => substitutions += ((dest, base, index)) + case (STUR(dest, base), index) => substitutions += ((dest, base, index)) + + // Given a load, check if the register is still stored in the substitutions, if so then the load-store is redundant + case (LDP(dest, src, base), index) => + substitutions.find { case (operand, reg, _) => operand == dest && reg == base } match { + case Some((_, _, fstIndex)) => + substitutions.find { case (operand, reg, _) => operand == src && reg == base } match { + case Some((_, _, sndIndex)) => + removalList += sndIndex; removalList += index + case None => + } + case None => + } + + case (LDR(dest, base, _, _), index) => + substitutions.find { case (operand, reg, _) => operand == dest && reg == base } match { + case Some((_, _, fstIndex)) => + removalList += fstIndex; removalList += index + case None => + } + + case (LDRB(dest, base, _), index) => + substitutions.find { case (operand, reg, _) => operand == dest && reg == base } match { + case Some((_, _, fstIndex)) => + removalList += fstIndex; removalList += index + case None => + } + + case (LDUR(dest, base), index) => + substitutions.find { case (operand, reg, _) => operand == dest && reg == base } match { + case Some((_, _, fstIndex)) => + removalList += fstIndex; removalList += index + case None => + } + // Consider all cases where the register is modified, and remove the stored value + case (MoveOps(dest, _), _) => substitutions.filterInPlace(_._1 != dest) + case (Move(dest, _), _) => substitutions.filterInPlace(_._1 != dest) + case (ADD(dest, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (SUB(dest, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (SMULL(dest, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (SDIV(dest, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (ADDS(dest, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (SUBS(dest, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (MSUB(dest, _, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (NEGS(dest, _), _) => substitutions.filterInPlace(_._1 != dest) + case (EOR(dest, _, _), _) => substitutions.filterInPlace(_._1 != dest) + case (ADRP(dest, _), _) => substitutions.filterInPlace(_._1 != dest) + case (ADR(dest, _), _) => substitutions.filterInPlace(_._1 != dest) + case (CSET(dest, _), _) => substitutions.filterInPlace(_._1 != dest) + case (CSEL(dest, _, _, _), _) => substitutions.filterInPlace(_._1 != dest) + + // Clear the substitutions on branches given any register may be manipulated in the callee + case (B(_), _) => substitutions.clear() + case (BL(_, _), _) => substitutions.clear() + case (BCond(_, _, _), _) => substitutions.clear() + case (CBZ(_, _), _) => substitutions.clear() + + case (Label(_), _) => substitutions.clear() + + case _ => + }) + + instructions.zipWithIndex.collect { + case (instr, index) if !removalList.contains(index) => + instr + } + } + + // When encountering a move, add the dest and src to a substitutions. if that dest is used in another instruction then substitute the dest with src + // given the original src remains unchanged. We then do a second pass that removes the redundant moves. + private def removeRedundantMoves(instructions: List[Instruction]): List[Instruction] = { + given allocator: RegisterAllocator = new RegisterAllocator() + val substitutions = mutable.ListBuffer[((Allocated | Operand, Allocated | Operand), Int)]() // List of (dest, src) to index + val substitutedLines = mutable.ListBuffer[((Allocated | Operand, Allocated | Operand), (Int, Int))]() // List of substitutions to their initial and subbed line + val removalList = mutable.ListBuffer[Int]() // List of indexes to remove + + // Up until the dest/src in original move has been changed, you can substitute the dest for the src in subsequent instructions + def addSubstitution(dest: Allocated, src: List[Allocated | Operand], index: Int): Unit = { + src.foreach { source => + substitutions.find(_._1._1 == source) match { + case Some(((srcDest, sub), subIndex)) => + substitutedLines += (((srcDest, sub), (index, subIndex))) + removalList += subIndex + case None => + } + } + // removes the dest from the substitutions list as both destination and source if it is changed + substitutions.filterInPlace(_._1._1 != dest) + substitutions.filterInPlace(_._1._2 != dest) + } + + // Given a dest, src, and index, substitute the dest with src in the instructions + // If operandBool is true, then other types of operands (other than Reg) are allowed in operands 2 onwards + def useSubstitution(dest: Reg, src: List[Operand], index: Int, operandBool: Boolean): List[Operand] = { + val indexes = substitutedLines.filter(_._2._1 == index).map(_._2) + val subs = substitutedLines.filter(_._2._1 == index).map(_._1) + val mutableSrc = mutable.ListBuffer(src*) + subs.foreach { + case (subDest, sub: Reg) => + mutableSrc.zipWithIndex.foreach { case (source, idx) => + if (subDest == source) mutableSrc(idx) = sub + } + case (subDest, sub: Operand) if operandBool => + mutableSrc.zipWithIndex.foreach { case (source, idx) => + if (idx != 0 && subDest == source) mutableSrc(idx) = sub + } + case _ => + + } + mutableSrc.zip(src).zip(indexes).foreach { case ((a, b), i) => + if (a == b) { + substitutedLines.filterInPlace(_._2 != i) + removalList.filterInPlace(_ != i._2) + } + } + mutableSrc.toList + } + + // Initial pass, where substitutions are added and instructions are checked to see whether they can be substituted + instructions.zipWithIndex.foreach { + case (MoveOps(dest, src), index) if dest == src => + removalList += index + case (Move(dest, src), index) if dest == src => + removalList += index + + case (MoveOps(dest, src), index) => + src match { + // Immediate needs to be explicitly dealt with to avoid overflow substitution + case Immediate(value) => + val valInt = value.toInt + if (valInt < 0 || valInt >= 0xFFFF) { + substitutions.filterInPlace(_._1._1 != dest) + substitutions.filterInPlace(_._1._2 != dest) + } else { + addSubstitution(dest, List(src), index) + substitutions += (((dest, src), index)) + } + case _ => + addSubstitution(dest, List(src), index) + substitutions += (((dest, src), index)) + } + + case (Move(dest, src), index) => addSubstitution(dest, List(src), index) + + case (MSUB(dest, src1, src2, src3), index) => substitutions.clear() + case (CSEL(dest, src1, src2, src3), index) => substitutions.clear() + + case (ADD(dest, src1, src2), index) => addSubstitution(dest, List(src1, src2), index) + case (ADDS(dest, src1, src2), index) => addSubstitution(dest, List(src1, src2), index) + case (SUB(dest, src1, src2), index) => addSubstitution(dest, List(src1, src2), index) + case (SUBS(dest, src1, src2), index) => addSubstitution(dest, List(src1, src2), index) + case (SMULL(dest, src1, src2), index) => addSubstitution(dest, List(src1, src2), index) + case (SDIV(dest, src1, src2), index) => addSubstitution(dest, List(src1, src2), index) + case (EOR(dest, src1, src2), index) => addSubstitution(dest, List(src1, src2), index) + + case (NEGS(dest, src), index) => addSubstitution(dest, List(src), index) + + // Although these instructions do not use operand registers, addSubsitutions is still used to clear substitutions based on destination + case (ADRP(dest, src), index) => addSubstitution(dest, List(), index) + case (ADR(dest, src), index) => addSubstitution(dest, List(), index) + case (CSET(dest, src), index) => addSubstitution(dest, List(), index) + + case (Compare(_, _, _), _) => + case (CMP(_, _, _), _) => + case (TST(_, _), _) => + case (Comment(_, _), _) => + case (Directive(_, _), _) => + case (EOF(), _) => + case (RET(), _) => + + // Clear substitutions on any instruction that hasnt been explicitly handled to maintain consistency. + case x => substitutions.clear() + } + + // Second pass, where instructions are substituted given they were added in the first pass + val substituted = instructions.zipWithIndex.collect { + case (MoveOps(dest, src), index) if substitutedLines.exists(_._2._1 == index) => + val indexes = substitutedLines.find(_._2._1 == index).get._2 + val sub = substitutedLines.find(_._2._1 == index).get._1._2 + sub match { + case x : Operand => MoveOps(dest, x) + case _ => + substitutedLines.filterInPlace(_._2 != indexes) + removalList.filterInPlace(_ != indexes._2) + MoveOps(dest, src) + } + case (Move(dest, src), index) if substitutedLines.exists(_._2._1 == index) => + val indexes = substitutedLines.find(_._2._1 == index).get._2 + val sub = substitutedLines.find(_._2._1 == index).get._1._2 + sub match { + case x : Allocated => Move(dest, x) + case x : Operand => + dest match { + case y : Reg => MoveOps(y, x) + case _ => + substitutedLines.filterInPlace(_._2 != indexes) + removalList.filterInPlace(_ != indexes._2) + Move(dest, src) + } + } + + // We call useSubstitution for generic cases on all operands, and reconstruct instruction with substituted operands. + case (ADD(dest, src1, src2), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src1, src2), index, true) + ADD(dest, srcs(0), srcs(1)) + + case (ADDS(dest, src1, src2), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src1, src2), index, true) + ADDS(dest, srcs(0), srcs(1)) + + case (SUB(dest, src1, src2), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src1, src2), index, true) + SUB(dest, srcs(0), srcs(1)) + + case (SUBS(dest, src1, src2), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src1, src2), index, true) + SUBS(dest, srcs(0), srcs(1)) + + case (SMULL(dest, src1, src2), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src1, src2), index, false) + SMULL(dest, srcs(0), srcs(1)) + + case (SDIV(dest, src1, src2), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src1, src2), index, false) + SDIV(dest, srcs(0), srcs(1)) + + case (EOR(dest, src1, src2), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src1, src2), index, true) + EOR(dest, srcs(0), srcs(1)) + + case (NEGS(dest, src), index) if substitutedLines.exists(_._2._1 == index) => + val srcs = useSubstitution(dest, List(src), index, false) + NEGS(dest, srcs(0)) + + case (instr, index) => + instr + } + + // Finally, return the instructions with the redundant moves removed. + substituted.zipWithIndex.collect { + case (instr, index) if !removalList.contains(index) => + instr + } + } +} diff --git a/src/main/wacc/frontend/semantic/environment.scala b/src/main/wacc/frontend/semantic/environment.scala new file mode 100644 index 0000000..fcb114f --- /dev/null +++ b/src/main/wacc/frontend/semantic/environment.scala @@ -0,0 +1,185 @@ +package wacc.frontend.semantic + +import wacc.frontend.syntax.ast._ +import wacc.backend.exprType +import scala.collection.mutable + +object environment { + trait EnvTrait { + def lookup(name: String): Option[Symbol] + def lookupType(id: String): Option[Type] + } + + // An environment for variable (and parameter) declarations + class Env(val parent: Option[Env]) extends EnvTrait { + val vars: mutable.Map[String, VarSymbol] = mutable.Map.empty + + override def lookup(name: String): Option[VarSymbol] = vars.get(name) match { + case None => parent.flatMap(_.lookup(name)) + case some => some + } + + override def lookupType(id: String): Option[Type] = { + + // For environments, we check on their raw input, not on the hashed value + val idKey = id.replaceAll("#\\d+$", "") + lookup(idKey) match { + case None => None + case Some(VarSymbol(_, _, _type, _)) => Some(_type) + } + } + + def add(sym: VarSymbol)(using errors: mutable.ListBuffer[SemanticError]): Unit = { + if (!vars.contains(sym.id)) { + vars(sym.id) = sym + } else { + // Illegal declaration + errors += RedeclarationError(sym.pos, sym.id, inFunction = false)(using this) + } + } + } + + // Global environment for functions + class GlobalFuncEnv extends EnvTrait { + + // A map from the base identifier (not renamed) to a set of Tuples of + // n-arity of Types + val funcPoolMap: mutable.Map[String, mutable.ListBuffer[(List[Type], String)]] = mutable.Map.empty + + // A map from the unique identifier to its func symbol + val funcs: mutable.Map[String, FuncSymbol] = mutable.Map.empty + + // Looks up the function and may return its symbol + override def lookup(id: String): Option[FuncSymbol] = funcs.get(id) + + override def lookupType(id: String): Option[Type] = funcs.get(id) match { + case None => None + case Some(FuncSymbol(_, _, _type, _, _)) => Some(_type) + } + + /** + * Performs lookup for accessing the correct unique symbol for calls + * Does a rudimentary type analysis to match to the correct function (we don't actually check if the typing is correct) + */ + def callLookup(id: String, args: List[Expr], pos: (Int, Int), localEnv: Env) + (using errors: mutable.ListBuffer[SemanticError]): Option[FuncSymbol] = { + + // Create a list of n-arity of Types where n is the length of args + val argSignature = args.map(exprType(_)(using localEnv)) + + // Get the number of matched functions and the last matched uniqueIds + val (matchedFunctions, uniqueId) = funcPoolMap.get(id) match { + case None => (0, "") + case Some(funcSignatures) => countMatches(funcSignatures.toList, argSignature) + } + + // Match on the number of functions matched + // (Greater than 1 means ambiguity) + matchedFunctions match { + case 0 => + // Undefined Function + errors += UndefinedFuncError(pos, id) + None + + // Finally, get the uniqueId + case 1 => funcs.get(uniqueId) + + case _ => + // Ambiguous Function call + errors += AmbiguousFuncCallError(pos, id) + None + } + } + + // Potentially adds a FuncSymbol to the funcs map but first checks if its parameters exist in + // the pool map. If they do, then we add a redeclaration error + def add(funcSymbol: FuncSymbol)(using errors: mutable.ListBuffer[SemanticError]): Unit = { + + // Extract needed parts of funcSymbol + val FuncSymbol(id, uniqueId, _, paramSymbs, pos) = funcSymbol + + // Create a list of n-arity of Types where n is the length of parameters + val paramTypes = getParamTypes(paramSymbs) + + // Find the list of function signatures from the funcPoolMap + val funcSignatures = funcPoolMap.getOrElse(id, mutable.ListBuffer.empty) + + + val (matchedFunctions, _) = countMatches(funcSignatures.toList, paramTypes) + + matchedFunctions match { + + // We now check the uniqueness of paramTypes + case 0 => + + // Create the tuple of paramTypes with the unique id of the function + val typeWithId = (paramTypes, uniqueId) + funcPoolMap.getOrElseUpdate(id, mutable.ListBuffer.empty) += typeWithId + funcs += (uniqueId -> funcSymbol) + + // Illegal declaration + case _ => + errors += RedeclarationError(pos, id, inFunction = true, paramTypes)(using this) + funcs += (id -> funcSymbol) + } + } + + /** + * Finds the number of matches that an argSignature makes with the funcSignatures that are known to the program thus far + * Returns the number of matches and the last seen unique id + */ + def countMatches(funcSignatures: List[(List[Type], String)], argSignature: List[Type]): (Int, String) = { + + // Get the candidate functions with the same arity as the called function + val matchCandidates = funcSignatures.filter(_._1.size == argSignature.size) + var matchedFunctions = 0 + var uniqueId = "" + + matchCandidates.foreach { candidate => + + // For each candidate, we check if the signatures match + val (paramSignature, uId) = candidate + val isMatch = argSignature.zip(paramSignature).foldLeft(true) { (acc, zippedSignature) => + + // Extract the arg and paramter type, then see if the argument type casts to the parameter + val (argType, paramType) = zippedSignature + (argType typecast Some(paramType)) && acc + } + + // We update the functions matched and unique id + if (isMatch) { + matchedFunctions += 1 + uniqueId = uId + } + } + (matchedFunctions, uniqueId) + } + + /** + * Create a list of the types that the parameter has + */ + private def getParamTypes(paramSymbs: List[VarSymbol]): List[Type] = paramSymbs.map { paramSym => + + // Extract the paramType from paramSym + val VarSymbol(_, _, paramType, _) = paramSym + + // Output the paramType + paramType + } + } + + // Global environment for main body + class GlobalMainEnv extends EnvTrait { + val vars: mutable.Map[String, VarSymbol] = mutable.Map.empty + + def lookup(id: String): Option[Symbol] = vars.get(id) + override def lookupType(id: String): Option[Type] = vars.get(id) match { + case Some(VarSymbol(_, _, _type, _)) => Some(_type) + case None => None + } + + def add(_var: VarSymbol)(using errors: mutable.ListBuffer[SemanticError] = mutable.ListBuffer.empty): Unit = { + vars += (_var.uniqueName -> _var) + } + } +} diff --git a/src/main/wacc/frontend/semantic/renamer.scala b/src/main/wacc/frontend/semantic/renamer.scala new file mode 100644 index 0000000..40012d1 --- /dev/null +++ b/src/main/wacc/frontend/semantic/renamer.scala @@ -0,0 +1,274 @@ +package wacc.frontend.semantic + +import scala.collection.mutable +import wacc.frontend.semantic.environment._ +import wacc.frontend.syntax.ast._ + +class Renamer { + + // A counter to generate unique names + private var counter: Int = 0 + + private def generateUnique(name: String): String = { + counter += 1 + s"$name#$counter" + } + + // Renames the entire program, returning a new WProgram with uniquely renamed identifiers + // It first updates the global function environment, then renames the main block and each function + def renameProgram(program: WProgram): + (WProgram, mutable.ListBuffer[SemanticError], GlobalFuncEnv, GlobalMainEnv) = { + + // Mutable list of errors found while traversing AST for renaming + // (Would be populated with scope errors for now) + given errors: mutable.ListBuffer[SemanticError] = mutable.ListBuffer.empty + + // Global Environments for both Functions and the Main body + given gFuncEnv: GlobalFuncEnv = new GlobalFuncEnv + given gMainEnv: GlobalMainEnv = new GlobalMainEnv + + // Create a new environment for starting a block block + { + given localEnv: Env = new Env(None) + given inFunction: Boolean = false + + // Rename each function. + val newFuncs = renameFuncs(program.funcs) + + // Rename main block statements. + val newStats = program.stats.map(renameStmt) + + // We'll keep the errors found so far and a new AST with renamed nodes + (WProgram(program.pos, program.imports, newFuncs, newStats), errors, gFuncEnv, gMainEnv) + } + } + + /// Renames all function definitions + def renameFuncs(funcs: List[Func]) + (using errors: mutable.ListBuffer[SemanticError], + gFuncEnv: GlobalFuncEnv, gMainEnv: GlobalMainEnv): List[Func] = { + + // Contains a funcSymbol with a list of its parameters, body, and associated environment + val fGroups: List[(FuncSymbol, List[Param], List[Stmt], Env)] = funcs.map { f => + + // Gen a unique name for the function + val uniqueFuncName = generateUnique(f.id.id) + + // Get the param symbol and param lists at the same time to feed into + // making a FuncSymbol and Func + val (paramSymbols, newParams) = f.args.map { p => + val uniqueParamName = generateUnique(p.id.id) + val newIdent = Ident(p.id.pos, uniqueParamName) + (VarSymbol(p.id.id, uniqueParamName, p.t, p.pos), Param(p.pos, p.t, newIdent)) + }.unzip + + // Create a funcSym using the paramSymbols and uniqueFuncName created and then add to + // GlobalFuncEnv + val funcSym = FuncSymbol(f.id.id, uniqueFuncName, f.t, paramSymbols, f.id.pos) + gFuncEnv.add(funcSym) + + // Create an environment to add the paramSymbols + val funcEnv = new Env(None) + paramSymbols.map(funcEnv.add) + + (funcSym, newParams, f.body, funcEnv) + } + + val newFuncs = fGroups.map { fGroup => + val (funcSym, newParams, body, funcEnv) = fGroup + + given localEnv: Env = new Env(Some(funcEnv)) + given inFunction: Boolean = true + + // Create the new identity and body + val newIdent = Ident(funcSym.pos, funcSym.uniqueName) + val newBody = body.map((stmt: Stmt) => renameStmt(stmt)) + + Func(funcSym.pos, funcSym._type, newIdent, newParams, newBody) + } + newFuncs + } + + // Rename the elements within statements while checking the validity of identifiers + def renameStmt(stmt: Stmt) + (using localEnv: Env, inFunction: Boolean, errors: mutable.ListBuffer[SemanticError], + gFuncEnv: GlobalFuncEnv, gMainEnv: GlobalMainEnv): Stmt = { + + stmt match { + case Skip(pos) => Skip(pos) + case Assign(pos, lhs, rhs) => Assign(pos, renameLhs(lhs), renameRhs(rhs)) + case Read(pos, lhs) => Read(pos, renameLhs(lhs)) + case Free(pos, expr) => Free(pos, renameExpr(expr)) + case Return(pos, expr) => Return(pos, renameExpr(expr)) + case Exit(pos, expr) => Exit(pos, renameExpr(expr)) + case Print(pos, expr) => Print(pos, renameExpr(expr)) + case Println(pos, expr) => Println(pos, renameExpr(expr)) + + // Add a new symbol to the environment on declaration + case Declare(pos, t, ident, rhs) => { + val newRhs = renameRhs(rhs) + val uniqueName = generateUnique(ident.id) + val newIdent = Ident(ident.pos, uniqueName) + val varSym = VarSymbol(ident.id, uniqueName, t, ident.pos) + + // Add the new symbol to the local and global variable environment + localEnv.add(varSym) + gMainEnv.add(varSym) + Declare(pos, t, newIdent, newRhs) + } + + case If(pos, cond, thenCase, elseCase) => { + val newCond = renameExpr(cond) + // Create new environments for each branch. + val thenEnv = new Env(Some(localEnv)) + val newThen = { + given localEnv: Env = thenEnv + thenCase.map(renameStmt) + } + val elseEnv = new Env(Some(localEnv)) + val newElse = { + given localEnv: Env = elseEnv + elseCase.map(renameStmt) + } + If(pos, newCond, newThen, newElse) + } + + case While(pos, cond, body) => { + val newCond = renameExpr(cond) + val bodyEnv = new Env(Some(localEnv)) + val newBody = { + given localEnv: Env = bodyEnv + body.map(renameStmt) + } + While(pos, newCond, newBody) + } + + case Block(pos, stmts) => { + val blockEnv = new Env(Some(localEnv)) + val newStmts = { + given localEnv: Env = blockEnv + stmts.map(renameStmt) + } + Block(pos, newStmts) + } + + } + } + + // Renames all expressions which may contain identities and ignores literals + def renameExpr(expr: Expr) + (using localEnv: Env, inFunction: Boolean, errors: mutable.ListBuffer[SemanticError], + gFuncEnv: GlobalFuncEnv, gMainEnv: GlobalMainEnv): Expr = { + expr match { + case Not(x) => Not(renameExpr(x)) + case Neg(x) => Neg(renameExpr(x)) + case Len(x) => Len(renameExpr(x)) + case Ord(x) => Ord(renameExpr(x)) + case Chr(x) => Chr(renameExpr(x)) + + case Mul(x, y) => Mul(renameExpr(x), renameExpr(y)) + case Div(x, y) => Div(renameExpr(x), renameExpr(y)) + case Mod(x, y) => Mod(renameExpr(x), renameExpr(y)) + + case Add(x, y) => Add(renameExpr(x), renameExpr(y)) + case Sub(x, y) => Sub(renameExpr(x), renameExpr(y)) + + case Gt(x, y) => Gt(renameExpr(x), renameExpr(y)) + case Geq(x, y) => Geq(renameExpr(x), renameExpr(y)) + case Lt(x, y) => Lt(renameExpr(x), renameExpr(y)) + case Leq(x, y) => Leq(renameExpr(x), renameExpr(y)) + + case Eq(x, y) => Eq(renameExpr(x), renameExpr(y)) + case Neq(x, y) => Neq(renameExpr(x), renameExpr(y)) + + case And(x, y) => And(renameExpr(x), renameExpr(y)) + case Or(x, y) => Or(renameExpr(x), renameExpr(y)) + + case ident: Ident => localEnv.lookup(ident.id) match { + case Some(varSym) => Ident(ident.pos, varSym.uniqueName) + case None => { + // Undeclared Variable + errors += UndefinedValError(ident.pos, ident.id) + ident + } + } + + case ArrayElem(pos, id, indices) => localEnv.lookup(id.id) match { + case Some(varSym) => + ArrayElem(pos, Ident(id.pos, varSym.uniqueName), indices.map(renameExpr)) + case None => { + // Undeclared Array + errors += UndefinedValError(id.pos, id.id) + ArrayElem(pos, id, indices.map(renameExpr)) + } + } + + case IfExpr(pos, cond, thenBranch, elseBranch) => { + val newCond = renameExpr(cond) + val thenEnv = new Env(Some(localEnv)) + val newThen = { + given env: Env = thenEnv + renameExpr(thenBranch) + } + val elseEnv = new Env(Some(localEnv)) + val newElse = { + given env: Env = elseEnv + renameExpr(elseBranch) + } + IfExpr(pos, newCond, newThen, newElse) + } + + // The rest are literals so no identities to rename here + case rest => rest + } + } + + def renameRhs(rhs: Rhs) + (using localEnv: Env, inFunction: Boolean, errors: mutable.ListBuffer[SemanticError], + gFuncEnv: GlobalFuncEnv, gMainEnv: GlobalMainEnv): Rhs = { + rhs match { + case ArrayLiter(pos, elems) => ArrayLiter(pos, elems.map(renameExpr)) + case NewPair(pos, fst, snd) => NewPair(pos, renameExpr(fst), renameExpr(snd)) + case Call(pos, id, args) => { + val newArgs = args.map(renameExpr) + val funcSym = gFuncEnv.callLookup(id.id, newArgs, pos, localEnv).getOrElse { + FuncSymbol(id.id, generateUnique(id.id), AnyType, Nil, id.pos) + } + val newId = Ident(id.pos, funcSym.uniqueName) + Call(pos, newId, newArgs) + } + + case PairElem(pos, selector, lhs) => PairElem(pos, selector, renameLhs(lhs)) + + case expr: Expr => renameExpr(expr) + } + } + + def renameLhs(lhs: Lhs) + (using localEnv: Env, inFunction: Boolean, errors: mutable.ListBuffer[SemanticError], + gFuncEnv: GlobalFuncEnv, gMainEnv: GlobalMainEnv): Lhs = { + lhs match { + case ident: Ident => localEnv.lookup(ident.id) match { + case Some(varSym) => Ident(ident.pos, varSym.uniqueName) + case None => { + // Undeclared Variable + errors += UndefinedValError(ident.pos, ident.id) + ident + } + } + + case ArrayElem(pos, id, indices) => localEnv.lookup(id.id) match { + case Some(varSym) => + ArrayElem(pos, Ident(id.pos, varSym.uniqueName), indices.map(renameExpr)) + case None => { + // Undeclared Array + errors += UndefinedValError(id.pos, id.id) + ArrayElem(pos, id, indices.map(renameExpr)) + } + } + + case PairElem(pos, selector, lhs) => PairElem(pos, selector, renameLhs(lhs)) + } + } + +} \ No newline at end of file diff --git a/src/main/wacc/frontend/semantic/semanticAnalyser.scala b/src/main/wacc/frontend/semantic/semanticAnalyser.scala new file mode 100644 index 0000000..786b118 --- /dev/null +++ b/src/main/wacc/frontend/semantic/semanticAnalyser.scala @@ -0,0 +1,18 @@ +package wacc.frontend.semantic + +import wacc.frontend.semantic.environment.GlobalFuncEnv +import wacc.frontend.semantic.environment.GlobalMainEnv +import wacc.frontend.syntax.ast.WProgram + +def analyse(program: WProgram): + Either[(WProgram, GlobalFuncEnv, GlobalMainEnv), List[SemanticError]] = { + + val renamer = new Renamer() + val (renamedAST, errors, globalFuncEnv, globalMainEnv) = renamer.renameProgram(program) + val (renamedProgram, errs) = + typeChecking.validateAST(renamedAST, globalFuncEnv.funcs.toMap)(using globalMainEnv, errors) + errs match { + case Nil => Left((renamedProgram, globalFuncEnv, globalMainEnv)) + case _ => Right(errs) + } +} \ No newline at end of file diff --git a/src/main/wacc/frontend/semantic/semanticErrors.scala b/src/main/wacc/frontend/semantic/semanticErrors.scala new file mode 100644 index 0000000..59cb1cf --- /dev/null +++ b/src/main/wacc/frontend/semantic/semanticErrors.scala @@ -0,0 +1,156 @@ +package wacc.frontend.semantic + +import scala.io.Source +import wacc.frontend.semantic.environment._ +import wacc.frontend.syntax.ast.Type + +val SEMANTIC_ERROR_CODE = 200 + +sealed trait SemanticError { + def getPosition: (Int, Int) + def getMessage(fileName: String, path: String): String +} + +// ==================================================== +// ERRORS FOUND DURING SCOPE ANALYSIS +// ==================================================== + +case class UndefinedFuncError(pos: (Int, Int), id: String) extends SemanticError { + override def getPosition: (Int, Int) = pos + + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + s"Undefined function error in $fileName" + + s" (${pos._1},${pos._2}):\n function $id is undefined:" + + s"\n$line\n" + } +} + +// TODO: May add on the number of matches +case class AmbiguousFuncCallError(pos: (Int, Int), id: String) extends SemanticError { + override def getPosition: (Int, Int) = pos + + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + s"Ambiguous function call in $fileName" + + s" (${pos._1},${pos._2}):\n function call for $id is ambiguous:" + + s"\n$line\n" + } +} + +case class UndefinedValError(pos: (Int, Int), id: String)(using env: Env) + extends SemanticError { + override def getPosition: (Int, Int) = pos + + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + val unpackedEnv = unpackEnv(env) + val relevantInScope = s" relevant in-scope variables include:\n${unpackEnv(env)}\n | $line\n" + s"Scope error in $fileName (${pos._1},${pos._2}):\n" + + s" variable $id has not been declared in this scope:\n" + + s"${if (unpackedEnv.size > 3) then relevantInScope else " there are no relevant in-scope variables\n"}" + } + + // Return a list of `\n` separated declarations, each line `_type _ident`. + def unpackEnv(e: Env): String = gatherVars(e) + .map(sym => s" ${sym._type.getType} ${sym.id} (declared on line ${sym.pos._1})") + .mkString("\n") + + // Recursively collect all visible symbols from `env` up to outer parents. + def gatherVars(e: Env): List[VarSymbol] = { + e.vars.values.toList ++ + (e.parent match { + case Some(p) => gatherVars(p) + case None => Nil + }) + } +} + +case class RedeclarationError(pos: (Int, Int), id: String, inFunction: Boolean, paramTypes: List[Type] = Nil) + (using env: EnvTrait) extends SemanticError { + + override def getPosition: (Int, Int) = pos + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + if (inFunction) { + s"Function redefinition error in $fileName (${pos._1},${pos._2}):\n" + + s" illegal redefinition of function $id with parameters (${paramTypes.map(_.getType).mkString(", ")})\n" + + s" previously declared on " + + s"line ${env.lookup(id).get.pos._1}\n | $line\n" + } else { + s"Scope error in $fileName (${pos._1},${pos._2}):\n" + + s" illegal redeclaration of variable $id\n" + + s" previously declared (in this scope) on " + + s"line ${env.lookup(id).get.pos._1}\n | $line\n" + } + } +} + +// ==================================================== +// ERRORS FOUND DURING TYPE CHECKING +// ==================================================== + +case class InvalidTypeError(pos: (Int, Int), details: (String, String)) extends SemanticError { + override def getPosition: (Int, Int) = pos + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + s"Type error in $fileName (${pos._1},${pos._2}):\n" + + s" unexpected ${details._1}\n" + + s" expected ${details._2}\n" + + s" | $line\n" + } +} + +case class PairAssignmentError(pos: (Int, Int), details: (String, String)) extends SemanticError { + override def getPosition: (Int, Int) = pos + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + s"Type error in $fileName (${pos._1},${pos._2}):\n" + + s" ${details._2}\n" + + s" | $line\n" + } +} + +case class InvalidConditionError(pos: (Int, Int), details: (String, String)) extends SemanticError { + override def getPosition: (Int, Int) = pos + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + s"Condition error in $fileName (${pos._1},${pos._2}):\n" + + s" unexpected ${details._1}\n" + + s" ${details._2}\n" + + s" | $line\n" + } +} + +case class FunctionCallError(pos: (Int, Int), details: (String, String)) extends SemanticError { + override def getPosition: (Int, Int) = pos + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + s"Function call error in $fileName (${pos._1},${pos._2}):\n" + + s" ${details._2}\n" + + s" | $line\n" + } +} + +case class GeneralTypeError(pos: (Int, Int), details: (String, String)) extends SemanticError { + override def getPosition: (Int, Int) = pos + override def getMessage(fileName: String, path: String): String = { + val line = getLineFromFile(path, pos._1).getOrElse("").strip() + s"Type error in $fileName (${pos._1},${pos._2}):\n" + + s" unexpected ${details._1}\n" + + s" ${details._2}\n" + + s" | $line\n" + } +} + + +private def getLineFromFile(path: String, lineNumber: Int): Option[String] = { + try { + val source = Source.fromFile(path) + val line = source.getLines().drop(lineNumber - 1).nextOption() // Get the specific line + source.close() + line + } catch { + case _: Exception => None // Handle errors if file can't be read + } +} \ No newline at end of file diff --git a/src/main/wacc/frontend/semantic/symbols.scala b/src/main/wacc/frontend/semantic/symbols.scala new file mode 100644 index 0000000..c7278d2 --- /dev/null +++ b/src/main/wacc/frontend/semantic/symbols.scala @@ -0,0 +1,20 @@ +package wacc.frontend.semantic + +import wacc.frontend.syntax.ast._ + +// Base trait for a resolved symbol. +sealed trait Symbol { + val id: String + val _type: Type + val pos: (Int, Int) +} + +// A symbol representing a variable (or function parameter). +// The field `uniqueName` holds the new (alpha–converted) name. +case class VarSymbol(id: String, uniqueName: String, _type: Type, pos: (Int, Int)) + extends Symbol + +// A symbol representing a function. +// The field `uniqueName` holds the new (alpha–converted) name. +case class FuncSymbol(id: String, uniqueName: String, _type: Type, + params: List[VarSymbol], pos: (Int, Int)) extends Symbol \ No newline at end of file diff --git a/src/main/wacc/frontend/semantic/typeChecking.scala b/src/main/wacc/frontend/semantic/typeChecking.scala new file mode 100644 index 0000000..c645f53 --- /dev/null +++ b/src/main/wacc/frontend/semantic/typeChecking.scala @@ -0,0 +1,293 @@ +package wacc.frontend.semantic + +import wacc.frontend.semantic.environment.GlobalMainEnv +import wacc.frontend.syntax.ast._ +import scala.collection.mutable + +object typeChecking { + + // Chooses the correct error type + private def addSemanticError(pos: (Int, Int), offending: String, message: String, + errConstructor: ( (Int, Int), (String, String) ) => SemanticError) + (using errors: mutable.ListBuffer[SemanticError]): Unit = + errors += errConstructor(pos, (offending, message)) + + def validateAST(program: WProgram, fMap: Map[String, FuncSymbol]) + (using globalMainEnv: GlobalMainEnv, errors: mutable.ListBuffer[SemanticError]): + (WProgram, List[SemanticError]) = { + + given funcMap: Map[String, FuncSymbol] = fMap + + val WProgram(pos, imports, funcs, stmts) = program + funcs.foreach(validateFunc) + stmts.foreach(validateStmt) + + (program, errors.sortBy(semErr => semErr.getPosition).toList) + } + + def validateFunc(func: Func) + (using funcMap: Map[String, FuncSymbol], globalMainEnv: GlobalMainEnv, + errors: mutable.ListBuffer[SemanticError]): Unit = { + + val Func(pos, retType, ident, params, stmts) = func + stmts.foreach(validateStmt(_)(using funcMap, Some(retType), params)) + } + + def validateStmt(stmt: Stmt) + (using funcMap: Map[String, FuncSymbol], funcReturnType: Option[Type] = None, + paramList: List[Param] = Nil, globalMainEnv: GlobalMainEnv, + errors: mutable.ListBuffer[SemanticError]): Unit = stmt match { + case Skip(_) => () + case Print(pos, expr) => findExprType(pos, expr) + case Println(pos, expr) => findExprType(pos, expr) + + case Declare(pos, declType, ident, rhs) => + findRhsType(pos, rhs).foreach { rhsType => + if rhsType != PairPlaceholder && !(rhsType typecast Some(declType)) then + addSemanticError(pos, s"${declType.getType}", s"${rhsType.getType}", InvalidTypeError.apply) + } + + case Assign(pos, lhs, rhs) => (findLhsType(pos, lhs), findRhsType(pos, rhs)) match { + case (Some(PairPlaceholder), Some(PairPlaceholder)) => + addSemanticError(pos, s"", "Cannot assign unknown types (both PairPlaceholder)", + PairAssignmentError.apply) + + case (Some(lhsType), Some(rhsType)) => + // highlight: unify the “check if both AnyType” into a single guard + if lhsType == AnyType && rhsType == AnyType then + addSemanticError(pos, s"${lhsType.getType}", "Cannot assign AnyType to AnyType", GeneralTypeError.apply) + + // highlight: more concise checks for PairPlaceholder and PairType + val lhsIsPartialPair = + lhsType == PairPlaceholder && rhsType.isInstanceOf[PairType] + val rhsIsPartialPair = + rhsType == PairPlaceholder && lhsType.isInstanceOf[PairType] + + if !lhsIsPartialPair && !rhsIsPartialPair && + !(rhsType typecast Some(lhsType)) + then + addSemanticError(pos, s"${lhsType.getType}", s"${rhsType.getType}", InvalidTypeError.apply) + + case _ => () + } + + case Read(pos, lhs) => findLhsType(pos, lhs) match { + case Some(IntType) | Some(CharType) => + case lhsTypeOpt => addSemanticError(pos, s"${lhsTypeOpt.getOrElse(AnyType).getType}", + "Invalid type for Read (must be int or char)", GeneralTypeError.apply) + } + + case Free(pos, expr) => findExprType(pos, expr) match { + case Some(ArrayType(_)) | Some(PairType(_, _)) => () + case typeOpt => addSemanticError(pos, s"${typeOpt.getOrElse(AnyType).getType}", + "Invalid type cannot be freed (must be a pair or array)", GeneralTypeError.apply) + } + + case Return(pos, expr) => + if funcReturnType.isEmpty then + addSemanticError(pos, s"", "Return statement outside of function", PairAssignmentError.apply) + + findExprType(pos, expr).foreach { exprType => + if !(exprType typecast funcReturnType) then + addSemanticError(pos, + s"unexpected ${exprType.getType}\n expected ${funcReturnType.getOrElse(AnyType).getType}", + "Invalid return type", PairAssignmentError.apply) + } + + case Exit(pos, expr) => findExprType(pos, expr) match { + case Some(IntType) => () + case typeOpt => addSemanticError(pos, s"${typeOpt.getOrElse(AnyType).getType}", + "Exit expression must be an int", GeneralTypeError.apply) + } + + case Block(_, blockStmts) => blockStmts.foreach(validateStmt) + + case If(pos, cond, thenCase, elseCase) => findExprType(pos, cond) match { + case Some(BoolType) => + thenCase.foreach(validateStmt) + elseCase.foreach(validateStmt) + case typeOpt => addSemanticError(pos, s"${typeOpt.getOrElse(AnyType).getType}", + "Invalid condition type in If (must be bool)", InvalidConditionError.apply) + } + + case While(pos, cond, body) => findExprType(pos, cond) match { + case Some(BoolType) => body.foreach(validateStmt) + case typeOpt => addSemanticError(pos, s"${typeOpt.getOrElse(AnyType).getType}", + "Invalid condition type in While (must be bool)", InvalidConditionError.apply) + } + } + + def findExprType(pos: (Int, Int), expr: Expr) + (using params: List[Param] = Nil, globalMainEnv: GlobalMainEnv, + errors: mutable.ListBuffer[SemanticError]): Option[Type] = expr match { + case Ident(_, id) => + globalMainEnv.lookupType(id).orElse(params.find(_.id.id == id).map(_.t)) + + case IntLiter(_, _) => Some(IntType) + case StrLiter(_, _) => Some(StrType) + case BoolLiter(_, _) => Some(BoolType) + case CharLiter(_, _) => Some(CharType) + case PairLiter(_) => Some(PairPlaceholder) + + case Not(x) => if unaryOpTypeCheck(pos, x, List(BoolType)) then Some(BoolType) else None + case Neg(x) => if unaryOpTypeCheck(pos, x, List(IntType)) then Some(IntType) else None + case Len(x) => if unaryOpTypeCheck(pos, x, List(ArrayType(AnyType))) then Some(IntType) else None + case Ord(x) => if unaryOpTypeCheck(pos, x, List(CharType)) then Some(IntType) else None + case Chr(x) => if unaryOpTypeCheck(pos, x, List(IntType)) then Some(CharType) else None + + case Div(x, y) => if binOpTypeCheck(pos, x, y, List(IntType)) then Some(IntType) else None + case Mul(x, y) => if binOpTypeCheck(pos, x, y, List(IntType)) then Some(IntType) else None + case Mod(x, y) => if binOpTypeCheck(pos, x, y, List(IntType)) then Some(IntType) else None + case Add(x, y) => if binOpTypeCheck(pos, x, y, List(IntType)) then Some(IntType) else None + case Sub(x, y) => if binOpTypeCheck(pos, x, y, List(IntType)) then Some(IntType) else None + + case Geq(x, y) => if binOpTypeCheck(pos, x, y, List(IntType, CharType)) then Some(BoolType) else None + case Gt (x, y) => if binOpTypeCheck(pos, x, y, List(IntType, CharType)) then Some(BoolType) else None + case Lt (x, y) => if binOpTypeCheck(pos, x, y, List(IntType, CharType)) then Some(BoolType) else None + case Leq(x, y) => if binOpTypeCheck(pos, x, y, List(IntType, CharType)) then Some(BoolType) else None + + case Eq (x, y) => if binOpTypeCheck(pos, x, y, List(AnyType)) then Some(BoolType) else None + case Neq(x, y) => if binOpTypeCheck(pos, x, y, List(AnyType)) then Some(BoolType) else None + + case And(x, y) => if binOpTypeCheck(pos, x, y, List(BoolType)) then Some(BoolType) else None + case Or (x, y) => if binOpTypeCheck(pos, x, y, List(BoolType)) then Some(BoolType) else None + + case PairElem(_, _, lhs) => findLhsType(pos, lhs) + case ArrayElem(_, ident, exprs) => arrayAccessType(pos, ident, exprs) + + case IfExpr(pos, cond, thenBranch, elseBranch) => + val thenType = findExprType(pos, thenBranch) + val elseType = findExprType(pos, elseBranch) + if (thenType.exists(_ typecast elseType)) then thenType + else if (elseType.exists(_ typecast thenType)) then elseType + else { + addSemanticError(pos, s"${thenType.getOrElse(AnyType).getType}, ${elseType.getOrElse(AnyType).getType}", + "Incompatible types in If-Expr branches", GeneralTypeError.apply) + None + } + } + + def unaryOpTypeCheck(pos: (Int, Int), x: Expr, requiredTypes: List[Type]) + (using params: List[Param], globalMainEnv: GlobalMainEnv, + errors: mutable.ListBuffer[SemanticError]): Boolean = findExprType(pos, x) match { + case Some(lhsType) if requiredTypes.exists(_ typecast Some(lhsType)) => true + case typeOpt => + addSemanticError(pos, s"${typeOpt.getOrElse(AnyType).getType} ", "Invalid type in unary operator", InvalidTypeError.apply) + false + } + + def binOpTypeCheck(pos: (Int, Int), x: Expr, y: Expr, requiredTypes: List[Type]) + (using params: List[Param], globalMainEnv: GlobalMainEnv, + errors: mutable.ListBuffer[SemanticError]): Boolean = { + val lhsOpt = findExprType(pos, x) + val rhsOpt = findExprType(pos, y) + (lhsOpt, rhsOpt) match { + case (Some(lhsType), Some(rhsType)) + if (requiredTypes.exists(_ typecast lhsOpt) || requiredTypes == List(AnyType)) + && (lhsType typecast rhsOpt) => true + case _ => + addSemanticError(pos, s"got ${lhsOpt.getOrElse(AnyType).getType}, ${rhsOpt.getOrElse(AnyType).getType}", + s"Invalid types in binary operator", GeneralTypeError.apply) + false + } + } + + def findRhsType(pos: (Int, Int), rhs: Rhs) + (using funcMap: Map[String, FuncSymbol], params: List[Param] = Nil, + globalMainEnv: GlobalMainEnv, errors: mutable.ListBuffer[SemanticError]): + Option[Type] = rhs match { + + case ArrayLiter(_, elems) => + val elemTypes = elems.flatMap(e => findExprType(pos, e)) + elemTypes.distinct match { + case t :: Nil => Some(ArrayType(t)) + case Nil => Some(ArrayType(AnyType)) // empty array => AnyType + case multiple => + val lcaType = multiple.reduceLeft { (t1, t2) => + if t1.typecast(Some(t2)) then t2 + else if t2.typecast(Some(t1)) then t1 + else + addSemanticError(pos, s"[${elemTypes.map(elem => elem.getType).mkString(", ")}]", "Multiple incompatible types in array", + GeneralTypeError.apply) + AnyType + } + Some(ArrayType(lcaType)) + } + + case NewPair(_, fst, snd) => (findExprType(pos, fst), findExprType(pos, snd)) match { + case (Some(f), Some(s)) => Some(PairType(f, s)) + case _ => + addSemanticError(pos, s"", "One of those types is invalid for newpair", + PairAssignmentError.apply) + None + } + + case PairElem(_, selector, _) => findLhsType(pos, rhs.asInstanceOf[Lhs]) + + case Call(callPos, ident, args) => + val funcNonUniqueName = ident.id.replaceAll("#\\d+$", "") + funcMap.get(ident.id) match { + case Some(FuncSymbol(_, _, returnType, paramTypes, _)) => + if args.size != paramTypes.size then + addSemanticError(callPos, s"", s"Function ${funcNonUniqueName} called with wrong # of arguments", + FunctionCallError.apply) + val argTypes = args.flatMap(findExprType(pos, _)) + argTypes.zip(paramTypes).foreach { case (aType, pType) => + if !(aType typecast Some(pType._type)) then + addSemanticError(callPos, s"", + s"Argument type ${aType.getType} does not match param type ${pType._type.getType}", + FunctionCallError.apply) + } + Some(returnType) + case None => + addSemanticError(callPos, s"", s"Function ${funcNonUniqueName} not found", + FunctionCallError.apply) + None + } + + case expr: Expr => findExprType(pos, expr) + } + + def findLhsType(pos: (Int, Int), lhs: Lhs) + (using params: List[Param] = Nil, globalMainEnv: GlobalMainEnv, + errors: mutable.ListBuffer[SemanticError]): Option[Type] = lhs match { + case ident: Ident => findExprType(pos, ident) + + case ArrayElem(_, arrayIdent, indices) => arrayAccessType(pos, arrayIdent, indices) + + case PairElem(_, selector, subLhs) => + val subType = findLhsType(pos, subLhs) + // If it's a partial pair with PairPlaceholder in one slot, allow for PairPlaceholder + (selector, subType) match { + case (Fst, Some(PairType(PairPlaceholder, _))) => Some(PairPlaceholder) + case (Snd, Some(PairType(_, PairPlaceholder))) => Some(PairPlaceholder) + case (Fst, Some(PairType(t:Type, _))) => Some(t) + case (Snd, Some(PairType(_, t:Type))) => Some(t) + case _ => Some(AnyType) + } + } + + // Handle array–element type extraction, checking index types. + def arrayAccessType(pos: (Int, Int), ident: Ident, indices: List[Expr]) + (using params: List[Param] = Nil, globalMainEnv: GlobalMainEnv, + errors: mutable.ListBuffer[SemanticError]): Option[Type] = findExprType(pos, ident) match { + case Some(arrType) => + // Must all be Int + val validIndices = indices.forall(findExprType(pos, _) contains IntType) + if !validIndices then addSemanticError(pos, s"${arrType.getType}", "Array indices must be of type int", + GeneralTypeError.apply) + + // Peel off array layers + val finalType = indices.foldLeft(arrType) { + case (ArrayType(inner), _) => inner + case (nonArr, _) => + addSemanticError(pos, s"$ident", "Too many indices for array", PairAssignmentError.apply) + nonArr + } + Some(finalType) + case None | Some(_) => + addSemanticError(pos, s"", "Invalid array access (identifier not found or not an array)", + PairAssignmentError.apply) + None + } +} \ No newline at end of file diff --git a/src/main/wacc/frontend/syntax/ast.scala b/src/main/wacc/frontend/syntax/ast.scala new file mode 100644 index 0000000..5d4f9a2 --- /dev/null +++ b/src/main/wacc/frontend/syntax/ast.scala @@ -0,0 +1,466 @@ +package wacc.frontend.syntax + +import parsley.generic.{ParserBridge1, ParserBridge2, ParserBridge3, ParserBridge4/*, ParserBridge5*/} + +object ast { + // ==================================================== + // PROGRAM STRUCTURE + // ==================================================== + + // ⟨program⟩ ::= ‘begin’ ⟨func⟩* ⟨stmt⟩ ‘end’. Condenses into List[Stmt] + case class WProgram(pos: (Int, Int), imports: List[Import], funcs: List[Func], stats: List[Stmt]) + + // Import statement: ‘import’ ⟨string⟩ + case class Import(pos: (Int, Int), path: String) + + // A function definition: + // ⟨func⟩ ::= ⟨type⟩ ⟨ident⟩ ‘(’ ⟨param-list⟩? ‘)’ ‘is’ ⟨stmt⟩ ‘end’ + // ⟨param-list⟩ ::= ⟨param⟩ ( ‘,’ ⟨param⟩ )* + case class Func(pos: (Int, Int), t: Type, id: Ident, args: List[Param], body: List[Stmt]) + + // A function parameter: ⟨param⟩ ::= ⟨type⟩ ⟨ident⟩ + case class Param(pos: (Int, Int), t: Type, id: Ident) + + // ==================================================== + // STATEMENTS + // ==================================================== + sealed trait Stmt + + // A “skip” statement: ‘skip’ + case class Skip(pos: (Int, Int)) extends Stmt + + // Variable declaration: ⟨type⟩ ⟨ident⟩ ‘=’ ⟨rvalue⟩ + case class Declare(pos: (Int, Int), t: Type, ident: Ident, rhs: Rhs) extends Stmt + + // Assignment: ⟨lvalue⟩ ‘=’ ⟨rvalue⟩ + case class Assign(pos: (Int, Int), lhs: Lhs, rhs: Rhs) extends Stmt + + // Read statement: ‘read’ ⟨lvalue⟩ + case class Read(pos: (Int, Int), lhs: Lhs) extends Stmt + + // Free statement: ‘free’ ⟨expr⟩ + case class Free(pos: (Int, Int), expr: Expr) extends Stmt + + // Return statement: ‘return’ ⟨expr⟩ + case class Return(pos: (Int, Int), expr: Expr) extends Stmt + + // Exit statement: ‘exit’ ⟨expr⟩ + case class Exit(pos: (Int, Int), expr: Expr) extends Stmt + + // Sealed trait for both prints (Used for code gen) + sealed trait PrintBase extends Stmt { + def pos: (Int, Int) + def expr: Expr + } + + // Print statement: ‘print’ ⟨expr⟩ + case class Print(pos: (Int, Int), expr: Expr) extends PrintBase + + // Println statement: ‘println’ ⟨expr⟩ + case class Println(pos: (Int, Int), expr: Expr) extends PrintBase + + // If statement: ‘if’ ⟨expr⟩ ‘then’ ⟨stmt⟩ ‘else’ ⟨stmt⟩ ‘fi’ + case class If(pos: (Int, Int), expr: Expr, + thenCase: List[Stmt], elseCase: List[Stmt]) extends Stmt + + // While loop: ‘while’ ⟨expr⟩ ‘do’ ⟨stmt⟩ ‘done’ + case class While(pos: (Int, Int), expr: Expr, body: List[Stmt]) extends Stmt + + // Block statement: ‘begin’ ⟨stmt⟩ ‘end’ + case class Block(pos: (Int, Int), stmts: List[Stmt]) extends Stmt + + // ⟨stmt⟩ ‘;’ ⟨stmt⟩ is captured by List[Stmt] + + // ==================================================== + // LVALUE AND RVALUE + // ==================================================== + // Both can be expressions + sealed trait Lhs + sealed trait Rhs + + // ---------------------------------------------------- + // Expressions + // ---------------------------------------------------- + + sealed trait Expr extends Rhs + + // Literals + case class IntLiter(pos: (Int, Int), value: BigInt) extends Expr + case class BoolLiter(pos: (Int, Int), value: Boolean) extends Expr + case class CharLiter(pos: (Int, Int), value: Char) extends Expr + case class StrLiter(pos: (Int, Int), value: String) extends Expr + + // The ‘null‘ literal (for pairs) + case class PairLiter(pos: (Int, Int)) extends Expr + + // Array literal: ⟨array-liter⟩ ::= ‘[’ ( ⟨expr⟩ (‘,’ ⟨expr⟩)* )? ‘]’ + case class ArrayLiter(pos: (Int, Int), elems: List[Expr]) extends Rhs + + // ‘newpair’ ‘(’ ⟨expr⟩ ‘,’ ⟨expr⟩ ‘)’ + case class NewPair(pos: (Int, Int), fst: Expr, snd: Expr) extends Rhs + + // Function call: ‘call’ ⟨ident⟩ ‘(’ ⟨arg-list⟩? ‘)’ + case class Call(pos: (Int, Int), id: Ident, args: List[Expr]) extends Rhs + + // ---------------------------------------------------- + // Operators + // ---------------------------------------------------- + + // Descending Order of Precedence, with top paragraph being most tightly binding + + // Unary operators + + sealed trait UnOp extends Expr { + def expr: Expr + } + + case class IfExpr(pos: (Int, Int), cond: Expr, thenBranch: Expr, elseBranch: Expr) extends Expr + + // Prefix + case class Not(expr: Expr) extends UnOp + case class Neg(expr: Expr) extends UnOp + case class Len(expr: Expr) extends UnOp + case class Ord(expr: Expr) extends UnOp + case class Chr(expr: Expr) extends UnOp + + // Binary operators + sealed trait BinOp extends Expr { + def lhs: Expr + def rhs: Expr + } + + sealed trait DivOp extends BinOp + sealed trait CompOp extends BinOp + + // infix left + case class Mul(lhs: Expr, rhs: Expr) extends BinOp + + case class Div(lhs: Expr, rhs: Expr) extends DivOp + case class Mod(lhs: Expr, rhs: Expr) extends DivOp + + // infix left + case class Add(lhs: Expr, rhs: Expr) extends BinOp + case class Sub(lhs: Expr, rhs: Expr) extends BinOp + + // infix non + case class Gt(lhs: Expr, rhs: Expr) extends CompOp + case class Geq(lhs: Expr, rhs: Expr) extends CompOp + case class Lt(lhs: Expr, rhs: Expr) extends CompOp + case class Leq(lhs: Expr, rhs: Expr) extends CompOp + + // infix non + case class Eq(lhs: Expr, rhs: Expr) extends CompOp + case class Neq(lhs: Expr, rhs: Expr) extends CompOp + + // infix right + case class And(lhs: Expr, rhs: Expr) extends BinOp + case class Or(lhs: Expr, rhs: Expr) extends BinOp + + // ---------------------------------------------------- + // LHS: Left-hand sides for assignments + // ---------------------------------------------------- + // An identifier: ⟨ident⟩ + case class Ident(pos: (Int, Int), id: String) extends Lhs with Expr { + override def equals(obj: Any): Boolean = obj match { + case Ident(_, id2) => this.id == id2 + case _ => false + } + override def hashCode(): Int = id.hashCode + } + + // An array element access: ⟨array-elem⟩ + case class ArrayElem(pos: (Int, Int), id: Ident, indices: List[Expr]) extends Lhs with Expr + + // Pair element: ⟨pair-elem⟩ + sealed trait PairSelector + case object Fst extends PairSelector + case object Snd extends PairSelector + // A pair element is both an lvalue and an expression: + // ⟨pair-elem⟩ ::= ‘fst’ ⟨lvalue⟩ | ‘snd’ ⟨lvalue⟩ + case class PairElem(pos: (Int, Int), selector: PairSelector, expr: Lhs) extends Lhs with Expr + + // ==================================================== + // TYPES + // ==================================================== + + // For pair types, the elements in the declarations must be either a base type or an array type + // as (Nested pairs are “erased”). + // A separate trait to mark types that can appear as elements of a pair. + sealed trait PairElemType { + + /** + * A helper method to compare array subtypes. It returns true if x and y are exactly the same, + * or if x and y are further pair-compatible. + */ + private def arrayCompatible(x: PairElemType, y: PairElemType): Boolean = + (x == y) || ((x, y) match { + case (a: PairElemType, b: PairElemType) => a.paircast(Some(b)) + }) + + /** + * Attempt to “typecast” this type into the other (Option[PairElemType]). + * For example, 'AnyType' can match anything, an Array of 'AnyType' can + * match any array, etc. If the other is None, returns false. + */ + infix def typecast(other: Option[PairElemType]): Boolean = other match { + case None => false + + case Some(o) => (this, o) match { + // Pair placeholders can match a concrete pair (and vice versa) + case (PairType(_, _), PairPlaceholder) | (PairPlaceholder, PairType(_, _)) => true + + // For two actual PairTypes, defer to paircast + case (PairType(_, _), PairType(_, _)) => this.paircast(other) + + // An array of AnyType is compatible with any array + case (ArrayType(AnyType), ArrayType(_)) => true + + // Compare arrays element–wise (including deeper pair casting) + case (ArrayType(x), ArrayType(y)) => arrayCompatible(x, y) + + // "char[]" can be cast to "string" + case (ArrayType(CharType), StrType) => true + + // "AnyType" matches anything, or anything can be cast to "AnyType" + case (AnyType, _) => true + case (_, AnyType) => true + + // Otherwise, check for exact equality + case _ => this == o + } + } + + /** + * A specialized version of casting that strictly compares pairs. If it is not + * a pair scenario or an exactly matching type, this returns false. + */ + infix def paircast(other: Option[PairElemType]): Boolean = other match { + case None => false + case Some(o) => (this, o) match { + case (PairType(f1, s1), PairType(f2, s2)) => + (f1.paircast(Some(f2))) && (s1.paircast(Some(s2))) + + // Pair placeholders can match any concrete PairType + case (PairType(_, _), PairPlaceholder) => true + case (PairPlaceholder, PairType(_, _)) => true + + // Array[AnyType] can match any array + case (ArrayType(AnyType), ArrayType(_)) => true + + // For two arrays, they must be exactly the same. (No 'AnyType' logic here.) + case (ArrayType(x), ArrayType(y)) => x == y + + // Otherwise, they must be exactly equal + case _ => this == o + } + } + + def getType: String +} + + sealed trait Type extends PairElemType + + case object AnyType extends Type { override def getType: String = "any"} + + // Base types + case object IntType extends Type { + override def getType = "int" + } + case object BoolType extends Type { override def getType = "bool" } + case object CharType extends Type { override def getType = "char" } + case object StrType extends Type { override def getType = "string" } + + // Array type: any type followed by []. + case class ArrayType(elemType: Type) extends Type { + override def getType = s"${elemType.getType}[]" + } + + // I think this is needed for placeholder pair types? + // e.g: pair(int, pair) is valid but pair(pair(int, char), bool) is not + case object PairPlaceholder extends Type { + override def getType = "pair" + } + + // A fully–specified pair type. + case class PairType(fst: PairElemType, snd: PairElemType) extends Type { + // Custom equality: AnyType acts as a wildcard + override def equals(obj: Any): Boolean = obj match { + case PairType(f1, s1) => + (fst == AnyType || fst == f1) && (snd == AnyType || snd == s1) + case _ => false + } + + // Ensure that equal objects have the same hash code + override def hashCode(): Int = { + (if (fst == AnyType) 0 else fst.hashCode) ^ + (if (snd == AnyType) 0 else snd.hashCode) + } + + override def getType = s"pair(${fst.getType}, ${snd.getType})" + } + + // ==================================================== + // PARSER BRIDGES + // ==================================================== + + // We can have null extending base type or as a literal extending expr im not sure yet + // case object Null extends BaseType {override def getType() = "null"} + + object WProgram extends ParserBridge4[(Int, Int), List[Import], List[Func], List[Stmt], WProgram] + object Import extends ParserBridge2[(Int, Int), String, Import] + object Func extends ParserBridge3[(((Int, Int), Type), Ident), List[Param], List[Stmt], Func] { + def apply(tupleOfPosTypeIdent: (((Int, Int), Type), Ident), params: List[Param], body: List[Stmt]): + Func = { + + val ((pos, _type), ident) = tupleOfPosTypeIdent + new Func(pos, _type, ident, params, body) + } + override def labels: List[String] = List("function declaration") + } + object Param extends ParserBridge3[(Int, Int), Type, Ident, Param] + + object Skip extends ParserBridge2[(Int, Int), Any, Skip] { + override def apply(pos: (Int, Int), _x: Any): Skip = Skip(pos) + override def labels: List[String] = List("statement") + } + object Declare extends ParserBridge4[(Int, Int), Type, Ident, Rhs, Declare] { + override def labels: List[String] = List("statement") + } + object Assign extends ParserBridge3[(Int, Int), Lhs, Rhs, Assign] { + override def labels: List[String] = List("statement") + } + object Read extends ParserBridge2[(Int, Int), Lhs, Read] { + override def labels: List[String] = List("statement") + } + object Free extends ParserBridge2[(Int, Int), Expr, Free] { + override def labels: List[String] = List("statement") + } + object Return extends ParserBridge2[(Int, Int), Expr, Return] { + override def labels: List[String] = List("statement") + } + object Exit extends ParserBridge2[(Int, Int), Expr, Exit] { + override def labels: List[String] = List("statement") + } + object Print extends ParserBridge2[(Int, Int), Expr, Print] { + override def labels: List[String] = List("statement") + } + object Println extends ParserBridge2[(Int, Int), Expr, Println] { + override def labels: List[String] = List("statement") + } + object If extends ParserBridge4[(Int, Int), Expr, List[Stmt], List[Stmt], If] { + override def labels: List[String] = List("statement") + } + object While extends ParserBridge3[(Int, Int), Expr, List[Stmt], While] { + override def labels: List[String] = List("statement") + } + object Block extends ParserBridge2[(Int, Int), List[Stmt], Block] { + override def labels: List[String] = List("statement") + } + + object ArrayElem extends ParserBridge3[((Int, Int), Ident), Expr, List[Expr], ArrayElem] { + def apply(tupleOfPosIdent: ((Int, Int), Ident), head: Expr, rest: List[Expr]): ArrayElem = { + val (pos, ident) = tupleOfPosIdent + + // Prepend the rest with the head + new ArrayElem(pos, ident, head +: rest) + } + } + object Ident extends ParserBridge2[(Int, Int), String, Ident] { + override def labels: List[String] = List("identifier") + } + object IntLiter extends ParserBridge2[(Int, Int), BigInt, IntLiter] { + override def labels: List[String] = List("integer literal") + } + object BoolLiter extends ParserBridge2[(Int, Int), Boolean, BoolLiter] { + override def labels: List[String] = List("boolean literal") + } + object CharLiter extends ParserBridge2[(Int, Int), Char, CharLiter] { + override def labels: List[String] = List("character literal") + } + object StrLiter extends ParserBridge2[(Int, Int), String, StrLiter] { + override def labels: List[String] = List("string literal") + } + object PairLiter extends ParserBridge2[(Int, Int), Any, PairLiter] { + override def apply(pos: (Int, Int), _x: Any): PairLiter = PairLiter(pos) + override def labels: List[String] = List("pair literal") + } + + object ArrayLiter extends ParserBridge2[(Int, Int), List[Expr], ArrayLiter] { + override def labels: List[String] = List("array literal") + } + object NewPair extends ParserBridge3[(Int, Int), Expr, Expr, NewPair] + object Call extends ParserBridge3[(Int, Int), Ident, List[Expr], Call] { + override def labels: List[String] = List("function call") + } + + object Not extends ParserBridge1[Expr, Not] { + override def labels: List[String] = List("expression") + } + object Neg extends ParserBridge1[Expr, Neg] { + override def labels: List[String] = List("expression") + } + object Len extends ParserBridge1[Expr, Len] { + override def labels: List[String] = List("expression") + } + object Ord extends ParserBridge1[Expr, Ord] { + override def labels: List[String] = List("expression") + } + object Chr extends ParserBridge1[Expr, Chr] { + override def labels: List[String] = List("expression") + } + object IfExpr extends ParserBridge4[(Int, Int), Expr, Expr, Expr, IfExpr] { + override def labels: List[String] = List("expression") + } + object Mul extends ParserBridge2[Expr, Expr, Mul] { + override def labels: List[String] = List("binary operator") + } + object Div extends ParserBridge2[Expr, Expr, Div] { + override def labels: List[String] = List("binary operator") + } + object Mod extends ParserBridge2[Expr, Expr, Mod] { + override def labels: List[String] = List("binary operator") + } + + object Add extends ParserBridge2[Expr, Expr, Add] { + override def labels: List[String] = List("binary operator") + } + object Sub extends ParserBridge2[Expr, Expr, Sub] { + override def labels: List[String] = List("binary operator") + } + + object Gt extends ParserBridge2[Expr, Expr, Gt] { + override def labels: List[String] = List("binary operator") + } + object Geq extends ParserBridge2[Expr, Expr, Geq] { + override def labels: List[String] = List("binary operator") + } + object Lt extends ParserBridge2[Expr, Expr, Lt] { + override def labels: List[String] = List("binary operator") + } + object Leq extends ParserBridge2[Expr, Expr, Leq] { + override def labels: List[String] = List("binary operator") + } + + object Eq extends ParserBridge2[Expr, Expr, Eq] { + override def labels: List[String] = List("binary operator") + } + object Neq extends ParserBridge2[Expr, Expr, Neq] { + override def labels: List[String] = List("binary operator") + } + + object And extends ParserBridge2[Expr, Expr, And] { + override def labels: List[String] = List("binary operator") + } + object Or extends ParserBridge2[Expr, Expr, Or] { + override def labels: List[String] = List("binary operator") + } + + object PairType extends ParserBridge2[PairElemType, PairElemType, PairType] + object PairElem extends ParserBridge2[((Int, Int), PairSelector), Lhs, PairElem] { + def apply(tupleOfPosSelector: ((Int, Int), PairSelector), lhs: Lhs): PairElem = { + val (pos, selector) = tupleOfPosSelector + new PairElem(pos, selector, lhs) + } + } + object ArrayType extends ParserBridge1[Type, ArrayType] +} \ No newline at end of file diff --git a/src/main/wacc/frontend/syntax/imports.scala b/src/main/wacc/frontend/syntax/imports.scala new file mode 100644 index 0000000..dc03914 --- /dev/null +++ b/src/main/wacc/frontend/syntax/imports.scala @@ -0,0 +1,40 @@ +package wacc.frontend.syntax + +import ast._ +import scala.concurrent._ +import scala.concurrent.ExecutionContext.Implicits.global + +class ImportHandler { + // given a WProgram, add all the functions from the imports to the program + def addImports(program: WProgram, optimiseFlag: Boolean = false): WProgram = { + // If there are less than 4 imports, we can just process them sequentially, as the overhead of parallelism is not worth it + // This will be evaluated at each level, so nested imports will be processed in parallel if they exceed 3 imports, even if + // the top level does not + val minInputs = 4 + val importFuncs = if (!optimiseFlag || program.imports.length < minInputs) { + program.imports.map(_.path).map(addProgram).map(_.funcs).flatten + } else { + // Convert imports into a list of futures to be executed in parallel + val futures = program.imports.map(_.path).map { path => + Future(addProgram(path)) + } + + // Wait for all Futures to complete + Await.result(Future.sequence(futures), scala.concurrent.duration.Duration.Inf) + .flatMap(_.funcs) + } + + program.copy(funcs = program.funcs ++ importFuncs) + } + + // given a file path, parse the file and return the WProgram + def addProgram(path: String): WProgram = { + parser.parseFile(path) match { + case parsley.Failure(msg) => { + println(s"\nSyntax Error $msg in file $path") + sys.exit(SYNTAX_ERROR_CODE) + } + case parsley.Success(progFromSyntax: WProgram) => addImports(progFromSyntax) + } + } +} \ No newline at end of file diff --git a/src/main/wacc/frontend/syntax/lexer.scala b/src/main/wacc/frontend/syntax/lexer.scala new file mode 100644 index 0000000..5aeb818 --- /dev/null +++ b/src/main/wacc/frontend/syntax/lexer.scala @@ -0,0 +1,69 @@ +package wacc.frontend.syntax + +import parsley.Parsley +import parsley.token.{Lexer, Basic} +import parsley.token.descriptions._ +import parsley.token.errors._ +import parsley.token.Unicode + +def isEnglishLetter(c: Char): Boolean = + c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' + +def isEnglishLetterOrDigit(c: Char): Boolean = + isEnglishLetter(c) || c.isDigit + +val errorConfig = new ErrorConfig { + override def labelSymbol: Map[String, LabelWithExplainConfig] = Map( + "[" -> Label("array index"), + "=" -> Label("assignment") + ) +} +object lexer { + val excludedChars = Set('\"', '\'', '\\') + val keys = Set( + "begin", "end", "is", "skip", "read", "free", "return", "exit", + "print", "println", "if", "then", "else", "fi", "while", "do", "done", + "fst", "snd", "newpair", "call", "int", "bool", "char", "string", + "pair", "true", "false", "null", "len", "ord", "chr" + ) + + val ops = Set( + "=", "!", "-", "*", "/", "%", "+", "-", + ">", ">=", "<", "<=", "==", "!=", "&&", "||" + ) + + private val desc = LexicalDesc.plain.copy( + nameDesc = NameDesc.plain.copy( + identifierStart = Basic(char => isEnglishLetter(char) || char == '_'), + identifierLetter = Basic(char => isEnglishLetterOrDigit(char) || char == '_') + ), + symbolDesc = SymbolDesc.plain.copy( + hardKeywords = keys, + hardOperators = ops, + ), + textDesc = TextDesc.plain.copy( + escapeSequences = EscapeDesc.plain.copy( + literals = Set('\\', '\'', '\"'), + mapping = Map("0" -> 0x00, "b" -> 0x08, "t" -> 0x09, "n" -> 0x0a, "f" -> 0x0c, + "r" -> 0x0d) + ), + graphicCharacter = Unicode(char => char >= ' '.toInt && !excludedChars.contains(char.toChar) + ) + ), + spaceDesc = SpaceDesc.plain.copy( + lineCommentStart = "#" + ), + + ) + + private val lexer = new Lexer(desc, errorConfig) + + // Define the primitive atoms of the grammar (done in terms of Scala types) + val integer = lexer.lexeme.integer.decimal32[BigInt] + val character = lexer.lexeme.character.ascii + val string = lexer.lexeme.string.ascii + val implicits = lexer.lexeme.symbol.implicits + val identifier = lexer.lexeme.names.identifier + + def fully[A](p: Parsley[A]): Parsley[A] = lexer.fully(p) +} \ No newline at end of file diff --git a/src/main/wacc/frontend/syntax/parser.scala b/src/main/wacc/frontend/syntax/parser.scala new file mode 100644 index 0000000..0543c8e --- /dev/null +++ b/src/main/wacc/frontend/syntax/parser.scala @@ -0,0 +1,259 @@ +package wacc.frontend.syntax + +import ast._ +import lexer._ +import lexer.implicits.implicitSymbol +import parsley.{Parsley, Result} +import parsley.combinator.{sepBy, sepBy1, ifS} +import parsley.errors.combinator._ +import parsley.expr.{precedence, Ops, Prefix, InfixL, InfixN, InfixR} +import parsley.position.pos +import Parsley.{many, atomic, pure, empty} +import scala.util.{Success, Failure} +import java.io.File + +object parser { + def parseFile(input: String): Result[String, WProgram] = parser.parseFile(new File(input)) match { + case Success(res) => res + case Failure(_) => parsley.Failure("File not recognised") + } + def parse(input: String): Result[String, WProgram] = parser.parse(input) + private val parser = fully(prog) + + // -------------------------- + // PROGRAM + // -------------------------- + + // prog ::= "begin" funcs* stmts "end" + private lazy val prog: Parsley[WProgram] = + WProgram(pos, many(_import), "begin" ~> many(func), stmts <~ "end") + + // -------------------------- + // IMPORTS + // -------------------------- + + // import ::= "import" string + private lazy val _import: Parsley[Import] = Import(pos, "import" ~> string) + + // -------------------------- + // FUNCTIONS + // -------------------------- + + // func ::= "(" paramList? ")" "is" stmts "end" + private lazy val func: Parsley[Func] = + Func(atomic(pos <~> _type <~> ident <~ "("), paramList <~ ")", "is" ~> stmts.filter(noReturns).explain(returnExplain) <~ "end") + + // paramList ::= param ("," param)* + private lazy val paramList: Parsley[List[Param]] = sepBy(param, ",") + + // param ::= + private lazy val param: Parsley[Param] = Param(pos, _type, ident) + + // predicate which returns true if a body returns/exits and false otherwise + private def noReturns(stmts: List[Stmt]): Boolean = stmts.lastOption match { + case Some(Return(_, _)) => true + case Some(Exit(_, _)) => true + case Some(While(_, _, body)) => noReturns(body) + case Some(If(_, _, thenStmts, elseStmts)) => noReturns(thenStmts) && + noReturns(elseStmts) + case Some(Block(_, stats)) => noReturns(stats) + case _ => false + } + + // -------------------------- + // IDENTIFIERS + // -------------------------- + + private lazy val ident: Parsley[Ident] = Ident(pos, identifier) + private lazy val intLiter: Parsley[IntLiter] = IntLiter(pos, integer) + private lazy val boolLiter: Parsley[BoolLiter] = + BoolLiter(pos, "true" ~> pure(true) | "false" ~> pure(false)) + private lazy val charLiter: Parsley[CharLiter] = CharLiter(pos, character) + private lazy val strLiter: Parsley[StrLiter] = StrLiter(pos, string) + private lazy val pairLiter: Parsley[PairLiter] = PairLiter(pos, "null") + private lazy val ifExpr: Parsley[IfExpr] = + IfExpr(pos, "if" ~> expr, "then" ~> expr, ("else"| _semiCheck) ~> expr <~ "fi") + + + // -------------------------- + // TYPES + // -------------------------- + + // simpleType ::= baseType | pairType | "(" _type ")" + // created to break the left recursion in type + private lazy val simpleType: Parsley[Type] = + baseType + | pairType + | ("(" ~> _type <~ ")") + + // baseType ::= "int" | "bool" | "char" | "string" + private lazy val baseType: Parsley[Type] = + ("int" as IntType) + | ("bool" as BoolType) + | ("char" as CharType) + | ("string" as StrType) + + // pairType ::= "pair" "(" pairElemType "," pairElemType ")" + private lazy val pairType: Parsley[Type] = + PairType("pair" ~> ("(" ~> pairElemType), "," ~> pairElemType <~ ")") + + // arraySuffix parses "[]" and returns a function wrapping a type in ArrayType + private lazy val arraySuffix: Parsley[Type => Type] = + ArrayType from ("[" ~> "]") + + // _type ::= simpleType (arraySuffix)* + private lazy val _type: Parsley[Type] = + simpleType.flatMap {t => + many(arraySuffix).map(fs => fs.foldLeft(t)((acc, f) => f(acc))) + } + + // pairElemType: a base (or array) type for pair elements, or the literal "pair" for erasure + private lazy val pairElemType: Parsley[PairElemType] = + (baseType.flatMap {t => + many(arraySuffix).map(fs => fs.foldLeft(t)((acc, f) => f(acc))) + }) + | ("pair" as PairPlaceholder) + + // -------------------------- + // EXPRESSIONS + // -------------------------- + + // Expression parser with precedence handling + private lazy val expr: Parsley[Expr] = + precedence( + (intLiter + | boolLiter + | charLiter + | strLiter + | pairLiter + | arrayElem + | ident + | ifExpr + | ("(" ~> expr <~ ")")).label("expression").explain(expressionExplain)) + ( + // If the token is an integer, return the empty combinator to escape this case so that + // it will be handled elsewhere + // Otherwise, wrap the expression (which is not a pure integer) in the Neg object + Ops(Prefix)(atomic(ifS(atomic(integer.hide) ~> pure(true)| pure(false), + empty, Neg from "-"))), + Ops(Prefix)(Not from "!"), + Ops(Prefix)(Len from "len"), + Ops(Prefix)(Ord from "ord"), + Ops(Prefix)(Chr from "chr"), + + Ops(InfixL)(Mul from "*"), + Ops(InfixL)(Mod from "%"), + Ops(InfixL)(Div from "/"), + + Ops(InfixL)(Add from "+"), + Ops(InfixL)(Sub from "-"), + + Ops(InfixN)(Gt from ">"), + Ops(InfixN)(Geq from ">="), + Ops(InfixN)(Lt from "<"), + Ops(InfixN)(Leq from "<="), + + Ops(InfixN)(Eq from "=="), + Ops(InfixN)(Neq from "!="), + + Ops(InfixR)(And from "&&"), + Ops(InfixR)(Or from "||") + ) + + // -------------------------- + // STATEMENTS + // -------------------------- + + private lazy val stmt: Parsley[Stmt] = + skipStmt + | declareStmt + | assignStmt + | readStmt + | freeStmt + | returnStmt + | exitStmt + | printStmt + | printlnStmt + | ifStmt + | whileStmt + | blockStmt + + // stmts ::= stmt (";" stmt)* + private lazy val stmts: Parsley[List[Stmt]] = sepBy1(stmt, ";") + + // "skip" statement. + private lazy val skipStmt: Parsley[Skip] = Skip(pos, "skip") + + // Declaration: "=" rVal + private lazy val declareStmt: Parsley[Declare] = Declare(pos, _type, ident <~ "=", rVal) + + // Assignment: lVal "=" rVal + private lazy val assignStmt: Parsley[Assign] = Assign(pos, lVal <~ "=", rVal) + + // Read statement: "read" lVal + private lazy val readStmt: Parsley[Read] = Read(pos, "read" ~> lVal) + + // Free statement: "free" expr + private lazy val freeStmt: Parsley[Free] = Free(pos, "free" ~> expr) + + // Return statement: "return" expr + private lazy val returnStmt: Parsley[Return] = Return(pos, "return" ~> expr) + + // Exit statement: "exit" expr + private lazy val exitStmt: Parsley[Exit] = Exit(pos, "exit" ~> expr) + + // Print statement: "print" expr + private lazy val printStmt: Parsley[Print] = Print(pos, "print" ~> expr) + + // Println statement: "println" expr + private lazy val printlnStmt: Parsley[Println] = Println(pos, "println" ~> expr) + + // If statement: "if" expr "then" stmts "else" stmts "fi" + private lazy val whileStmt: Parsley[While] = While(pos, "while" ~> expr, "do" ~> stmts <~ "done") + + // While statement: "while" expr "do" stmts "done" + private lazy val ifStmt: Parsley[If] = + If(pos, "if" ~> expr, "then" ~> stmts, ("else"| _semiCheck) ~> stmts <~ "fi") + + // Block statement: "begin" stmts "end" + private lazy val blockStmt: Parsley[Block] = Block(pos, "begin" ~> stmts <~ "end") + + // -------------------------- + // L-VALUES and R-VALUES + // -------------------------- + + // lVal ::= arrayElem | pairElem | lident + private lazy val lVal: Parsley[Lhs] = + arrayElem + | pairElem + | ident + + // arrayElem ::= lident ("[" expr "]")+ + private lazy val arrayElem: Parsley[ArrayElem] = ArrayElem(atomic(pos <~> ident <~ "["), expr <~ "]", many("[" ~> expr <~ "]")) + + // pairElem ::= ("fst" | "snd") lVal + private lazy val pairElem: Parsley[PairElem] = + PairElem(atomic(pos <~> (("fst" as Fst) | ("snd" as Snd))), lVal) + + // rVal ::= expr | arrayLiter | newPair | pairElem | call + private lazy val rVal: Parsley[Rhs] = + expr + | arrayLiter + | newPair + | pairElem + | call + + // arrayLiter ::= "[" exprs "]" + private lazy val arrayLiter: Parsley[ArrayLiter] = + ArrayLiter(pos, "[" ~> exprs <~ "]") + + // newPair ::= "newpair" "(" expr "," expr ")" + private lazy val newPair: Parsley[NewPair] = + NewPair(pos, "newpair" ~> ("(" ~> expr), "," ~> expr <~ ")") + + // call ::= "call" lident "(" exprs ")" + private lazy val call: Parsley[Call] = Call(pos, "call" ~> ident, "(" ~> exprs <~ ")") + + // exprs ::= (expr ("," expr)*)? + private lazy val exprs: Parsley[List[Expr]] = sepBy(expr, ",") +} diff --git a/src/main/wacc/frontend/syntax/syntaxErrors.scala b/src/main/wacc/frontend/syntax/syntaxErrors.scala new file mode 100644 index 0000000..4b37f10 --- /dev/null +++ b/src/main/wacc/frontend/syntax/syntaxErrors.scala @@ -0,0 +1,17 @@ +package wacc.frontend.syntax + +import parsley.character.char +import parsley.errors.patterns.VerifiedErrors + +val SYNTAX_ERROR_CODE = 100 + +val _semiCheck = char(';').verifiedExplain("semi-colons cannot be written between `if` and `else`") + +// Defining explanations for constructs that yield the same error explanation message +val expressionExplain = "expressions may start with integer, string, character or boolean\n" + + " literals; identifiers; unary operators; null; or parentheses.\n" + + " In addition, expressions may contain array indexing operations; and\n" + + " comparison, logical, and arithmetic operators" + +val bodyEndExplain = "all programs must be enclosed in begin ... end" +val returnExplain = "Missing a return on all exit paths" \ No newline at end of file diff --git a/src/test/wacc/codeGen.scala b/src/test/wacc/codeGen.scala new file mode 100644 index 0000000..ffaabb1 --- /dev/null +++ b/src/test/wacc/codeGen.scala @@ -0,0 +1,114 @@ +import org.scalatest.flatspec.AnyFlatSpec + +class codeGenTest extends CodeGeneratorTestSuite { + + /* Wrapper for uniform testing */ + def test(input: os.Path): (Boolean, String) = { + super.test(input, None, None) + } + + // IGNORE SET: Contains files/directories to ignore in testing + val ignoreSet: Set[os.Path] = Set() + + // ---------------------------------------------------- + // Testing programs with syntax errors + // ---------------------------------------------------- + + behavior of "TESTING CODE GEN IN syntaxErr" + + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxArray)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxBasic)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxExpressions)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxFunction)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxIf)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxLiteral)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxPairs)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxPrint)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxSequence)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxVariables)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSyntaxWhile)) + + // ---------------------------------------------------- + // Testing programs with semantic errors + // ---------------------------------------------------- + + behavior of "TESTING CODE GEN IN semanticErr" + + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticArray)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticExit)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticExpressions)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticFunction)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticIf)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticIO)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticMultiple)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticPairs)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticPrint)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticRead)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticScope)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticVariables)) + genTest("Code Generator", ignoreSet, test, "fail", os.list(invalidSemanticWhile)) + + // ---------------------------------------------------- + // Testing valid programs + // ---------------------------------------------------- + + behavior of "TESTING CODE GEN IN valid/basic/exit" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validBasicExit)) + + behavior of "TESTING CODE GEN IN valid/basic/skip" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validBasicSkip)) + + behavior of "TESTING CODE GEN IN valid/expressions" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validExpression)) + + behavior of "TESTING CODE GEN IN valid/function/nested_functions" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validFunctionNestFuns)) + + behavior of "TESTING CODE GEN IN valid/function/simple_functions" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validFunctionSimpFuns)) + + behavior of "TESTING CODE GEN IN valid/function/overload_functions" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validFunctionOverFuns)) + + behavior of "TESTING CODE GEN IN valid/if" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validIf)) + + behavior of "TESTING CODE GEN IN valid/IO/print" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validIOPrint)) + + behavior of "TESTING CODE GEN IN valid/IO/read" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validIORead)) + + behavior of "TESTING CODE GEN IN valid/IO/special" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validIOSpecial)) + + behavior of "TESTING CODE GEN IN valid/pairs" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validPair)) + + behavior of "TESTING CODE GEN IN valid/runtimeErr/arrayOutOfBounds" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validRTEArrOOB)) + + behavior of "TESTING CODE GEN IN valid/runtimeErr/badChar" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validRTEBadChar)) + + behavior of "TESTING CODE GEN IN valid/runtimeErr/divideByZero" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validRTEDivByZero)) + + behavior of "TESTING CODE GEN IN valid/runtimeErr/integerOverflow" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validRTEIntOverflow)) + + behavior of "TESTING CODE GEN IN valid/runtimeErr/nullDereference" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validRTENullDereference)) + + behavior of "TESTING CODE GEN IN valid/scope" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validScope)) + + behavior of "TESTING CODE GEN IN valid/sequence" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validSequence)) + + behavior of "TESTING CODE GEN IN valid/variables" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validVariables)) + + behavior of "TESTING CODE GEN IN valid/while" + genTest("Code Generator", ignoreSet, test, "succeed", os.list(validWhile)) +} diff --git a/src/test/wacc/codeGenTestConfig.scala b/src/test/wacc/codeGenTestConfig.scala new file mode 100644 index 0000000..04f66a4 --- /dev/null +++ b/src/test/wacc/codeGenTestConfig.scala @@ -0,0 +1,194 @@ +import org.scalatest.flatspec.AnyFlatSpec +import parsley._ +import scala.util.{Try, Success, Failure} +import wacc.backend.CodeGenerator +import wacc.extension.Peephole +import wacc.frontend.semantic.analyse +import wacc.frontend.syntax.ast.WProgram +import wacc.frontend.syntax.ImportHandler +import java.time.Instant + +// This is the trait for all code generating tests (CodeGen, Imports, Peephole) +trait CodeGeneratorTestSuite extends ParserTestSuite { + + /** + * Returns the success of generating a wacc file + * + * @param input Path to wacc file + * @return either (true, "") on a successful gen or (false, msg) on an unsuccessful gen + */ + def test(input: os.Path, peepholeOpt: Option[Peephole], importHandlerOpt: Option[ImportHandler], printParallelTests: Boolean = false): (Boolean, String) = { + parseFile(input) match { + case parsley.Success(prog: WProgram) => { + + // Adds the program to be analysed based on whether the import handler exists or not + val progToAnalyse = importHandlerOpt match { + case Some(importHandler) => + if (printParallelTests) { + var average = 0L + val testNum = 100 + println(s"Starting Non-Parallel import processing for ${input.last} over $testNum tests") + for (i <- 1 to testNum) { + val start = Instant.now() + importHandler.addImports(prog) + val end = Instant.now() + average += java.time.Duration.between(start, end).toNanos + } + println(s"Non-Parallel processing took: ${average/testNum} ns on average") + average = 0L + println(s"Starting Parallel import processing for ${input.last}") + for (i <- 1 to testNum) { + val start = Instant.now() + importHandler.addImports(prog, optimiseFlag = true) + val end = Instant.now() + average += java.time.Duration.between(start, end).toNanos + } + println(s"Parallel processing took: ${average/testNum} ns") + } + + importHandler.addImports(prog, optimiseFlag = printParallelTests) + case None => prog + } + + // Analyse the program and perform further checks after + analyse(progToAnalyse) match { + case Left(res) => + val fileName = input.last + val outputFile = s"${fileName.replace(".wacc", ".s")}" + val outputPath = os.pwd / "src" / "test" / "wacc" / "waccOutputs" / outputFile + + // Make the outputPath's folder if it doesn't already exist + os.makeDir.all(outputPath / os.up) + val (prog, fEnv, mEnv) = res + + // Create the code generator and peephole instances + val codeGenerator = new CodeGenerator() + + // Generate the asm without any possible peephole optimization + val unOptimizedAsm = codeGenerator.generate(prog, fEnv, mEnv) + + val asmCode = peepholeOpt match { + case Some(peephole) => peephole.optimize(unOptimizedAsm).map(_.emit).mkString("\n") + case None => unOptimizedAsm.map(_.emit).mkString("\n") + } + + // Writes to the folder location given that it doesn't exist or contains a different asm + if (!os.exists(outputPath) || os.read(outputPath) != asmCode) { + os.write.over(outputPath, asmCode) + } + + // Run checkAsm, then delete the folder containing the outputs regardless of the outcome + val result = + try { + checkAsm(input, outputPath) + } finally { + os.remove.all(outputPath / os.up) + } + result + + case Right(errs) => + val fileName = input.last + (false, errs.map(err => err.getMessage(fileName, input.toString)).mkString("\n")) + } + } + case parsley.Failure(msg) => (false, msg) + } + } + + def checkAsm(waccPath: os.Path, asmPath: os.Path): (Boolean, String) = { + // Read the WACC file lines + val lines = os.read.lines(waccPath) + + // Extract the expected exit code from a "# Exit:" block (default to 0 if not provided) + val expectedExit: Int = { + val maybeIndex = lines.indexWhere(_.trim == "# Exit:") + if (maybeIndex != -1 && lines.size > maybeIndex + 1) + Try(lines(maybeIndex + 1).stripPrefix("#").trim.toInt).getOrElse(0) + else 0 + } + + // Extract expected output lines from the "# Output:" block + // We take all lines after "# Output:" until we reach a blank line + val expectedOutput: List[String] = { + val startIndex = lines.indexWhere(_.trim == "# Output:") + if (startIndex != -1) + lines.drop(startIndex + 1) + .takeWhile(line => line.stripPrefix("#").trim.nonEmpty) + .map(_.stripPrefix("#").trim) + .toList + else Nil + } + + // Extract input (if any) from a line starting with "# Input:" + val maybeInput: Option[String] = + lines.find(_.trim.startsWith("# Input:")).map(_.stripPrefix("# Input:").trim) + + // Derive the assembly file name and the executable name + val asmFileName = asmPath.last + val executableName = asmFileName.stripSuffix(".s") + + // Get the directory containing the assembly file (this will be the working directory) + val asmDir = asmPath / os.up + + // Build the run command using qemu-aarch64 properly + val qemuString = s"qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./$executableName" + val runCommand = maybeInput match { + case Some(input) => + // Escape any single quotes to avoid shell issues + val safeInput = input.replace("'", "'\\''") + s"echo \"$safeInput\" | $qemuString" + case None => + s"echo '' | $qemuString" + } + + // Build the compile command using the cross compiler + val compileCommand = s"aarch64-linux-gnu-gcc -o $executableName -z noexecstack -march=armv8-a $asmFileName" + val execCommand = s"$compileCommand && $runCommand" + + // Execute the command using the shell, setting cwd to the asm directory + val resultTry = Try(os.proc("sh", "-c", execCommand) + .call(cwd = asmDir, stdout = os.Pipe, stderr = os.Pipe, check = false)) + + resultTry match { + case Failure(e) => (false, s"Exception while running command: ${e.getMessage}") + case Success(result) => + val rawOutput = result.out.text().trim.linesIterator.toList + val actualOutput = normaliseOutput(rawOutput) + val actualExit = result.exitCode + var errors = List.empty[String] + if (actualExit != expectedExit) { + errors ::= s"Expected exit code $expectedExit but got $actualExit." + } + if (actualOutput != expectedOutput) { + errors ::= s"Expected output:\n${expectedOutput.mkString("\n")}\nBut got:\n${actualOutput.mkString("\n")}" + } + if (errors.isEmpty) { + (true, "") + } else { + (false, errors.reverse.mkString("\n")) + } + } + } + + /** Normalises the output after executing generated code + * replacing addresses with #addrs# + * replacing runtime_errors with #runtime_error# + * + * Returns the normalised Output as a list of strings + */ + def normaliseOutput(rawOutput: List[String]): List[String] = { + val addressPattern = "0x[0-9A-Fa-f]+".r + val rtePattern = "^fatal error:.*$".r + + val outputWithAddrs = rawOutput.map { line => + addressPattern.replaceAllIn(line, "#addrs#") + } + + val outputWithRTEs = outputWithAddrs.map { line => + rtePattern.replaceAllIn(line, "#runtime_error#") + } + + outputWithRTEs + } + +} diff --git a/src/test/wacc/imports.scala b/src/test/wacc/imports.scala new file mode 100644 index 0000000..edc8a11 --- /dev/null +++ b/src/test/wacc/imports.scala @@ -0,0 +1,121 @@ +import org.scalatest.flatspec.AnyFlatSpec +import wacc.frontend.syntax.ImportHandler + +class importsTest extends CodeGeneratorTestSuite { + + /* Wrapper for uniform testing */ + def test(input: os.Path): (Boolean, String) = { + + // Create the importHandler instance + val importHandler = ImportHandler() + super.test(input, None, Some(importHandler)) + } + + // IGNORE SET: Contains files/directories to ignore in testing + val ignoreSet: Set[os.Path] = Set() + + // ---------------------------------------------------- + // Testing programs with syntax errors + // ---------------------------------------------------- + + behavior of "TESTING IMPORT HANDLER IN syntaxErr" + + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxArray)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxBasic)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxExpressions)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxFunction)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxIf)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxLiteral)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxPairs)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxPrint)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxSequence)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxVariables)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSyntaxWhile)) + + // ---------------------------------------------------- + // Testing programs with semantic errors + // ---------------------------------------------------- + + behavior of "TESTING IMPORT HANDLER IN semanticErr" + + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticArray)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticExit)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticExpressions)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticFunction)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticIf)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticIO)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticMultiple)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticPairs)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticPrint)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticRead)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticScope)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticVariables)) + genTest("Import Handler", ignoreSet, test, "fail", os.list(invalidSemanticWhile)) + + // ---------------------------------------------------- + // Testing valid programs + // ---------------------------------------------------- + + behavior of "TESTING IMPORT HANDLER IN valid/basic/exit" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validBasicExit)) + + behavior of "TESTING IMPORT HANDLER IN valid/basic/skip" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validBasicSkip)) + + behavior of "TESTING IMPORT HANDLER IN valid/expressions" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validExpression)) + + behavior of "TESTING IMPORT HANDLER IN valid/function/nested_functions" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validFunctionNestFuns)) + + behavior of "TESTING IMPORT HANDLER IN valid/function/simple_functions" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validFunctionSimpFuns)) + + behavior of "TESTING IMPORT HANDLER IN valid/function/overload_functions" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validFunctionOverFuns)) + + behavior of "TESTING IMPORT HANDLER IN valid/function/imported_functions" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validFunctionImports)) + + behavior of "TESTING IMPORT HANDLER IN valid/if" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validIf)) + + behavior of "TESTING IMPORT HANDLER IN valid/IO/print" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validIOPrint)) + + behavior of "TESTING IMPORT HANDLER IN valid/IO/read" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validIORead)) + + behavior of "TESTING IMPORT HANDLER IN valid/IO/special" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validIOSpecial)) + + behavior of "TESTING IMPORT HANDLER IN valid/pairs" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validPair)) + + behavior of "TESTING IMPORT HANDLER IN valid/runtimeErr/arrayOutOfBounds" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validRTEArrOOB)) + + behavior of "TESTING IMPORT HANDLER IN valid/runtimeErr/badChar" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validRTEBadChar)) + + behavior of "TESTING IMPORT HANDLER IN valid/runtimeErr/divideByZero" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validRTEDivByZero)) + + behavior of "TESTING IMPORT HANDLER IN valid/runtimeErr/integerOverflow" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validRTEIntOverflow)) + + behavior of "TESTING IMPORT HANDLER IN valid/runtimeErr/nullDereference" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validRTENullDereference)) + + behavior of "TESTING IMPORT HANDLER IN valid/scope" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validScope)) + + behavior of "TESTING IMPORT HANDLER IN valid/sequence" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validSequence)) + + behavior of "TESTING IMPORT HANDLER IN valid/variables" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validVariables)) + + behavior of "TESTING IMPORT HANDLER IN valid/while" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validWhile)) +} diff --git a/src/test/wacc/importsParallel.scala b/src/test/wacc/importsParallel.scala new file mode 100644 index 0000000..640d485 --- /dev/null +++ b/src/test/wacc/importsParallel.scala @@ -0,0 +1,23 @@ +import org.scalatest.flatspec.AnyFlatSpec +import wacc.frontend.syntax.ImportHandler + +class importsParallelTest extends CodeGeneratorTestSuite { + + /* Wrapper for uniform testing */ + def test(input: os.Path): (Boolean, String) = { + + // Create the importHandler instance + val importHandler = ImportHandler() + super.test(input, None, Some(importHandler), printParallelTests = false) + } + + // IGNORE SET: Contains files/directories to ignore in testing + val ignoreSet: Set[os.Path] = Set() + + // ---------------------------------------------------- + // Testing programs with syntax errors + // ---------------------------------------------------- + + behavior of "TESTING PARALLEL IMPORT HANDLER IN valid/function/imported_functions" + genTest("Import Handler", ignoreSet, test, "succeed", os.list(validFunctionImports)) +} diff --git a/src/test/wacc/parser.scala b/src/test/wacc/parser.scala new file mode 100644 index 0000000..06d3cbd --- /dev/null +++ b/src/test/wacc/parser.scala @@ -0,0 +1,171 @@ +import org.scalatest.flatspec.AnyFlatSpec +import parsley.{Success, Failure} + +class ParserTests extends ParserTestSuite { + + /** + * Returns the success of parsing a wacc file + * + * @param input Path to wacc file + * @return either (true, "") on a successful parse or (false, msg) on an unsuccessful parse + */ + def test(input: os.Path): (Boolean, String) = { + parseFile(input) match { + case Success(_) => (true, "") + case Failure(msg) => (false, msg) + } + } + + // IGNORE SET: Contains files/directories to ignore in testing + val ignoreSet: Set[os.Path] = Set() + + // ---------------------------------------------------- + // Testing programs with syntax errors + // ---------------------------------------------------- + + behavior of "TESTING PARSER IN syntaxErr/array" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxArray)) + + behavior of "TESTING PARSER IN syntaxErr/basic" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxBasic)) + + behavior of "TESTING PARSER IN syntaxErr/expressions" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxExpressions)) + + behavior of "TESTING PARSER IN syntaxErr/function" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxFunction)) + + behavior of "TESTING PARSER IN syntaxErr/if" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxIf)) + + behavior of "TESTING PARSER IN syntaxErr/literals" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxLiteral)) + + behavior of "TESTING PARSER IN syntaxErr/pairs" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxPairs)) + + behavior of "TESTING PARSER IN syntaxErr/print" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxPrint)) + + behavior of "TESTING PARSER IN syntaxErr/sequence" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxSequence)) + + behavior of "TESTING PARSER IN syntaxErr/variables" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxVariables)) + + behavior of "TESTING PARSER IN syntaxErr/while" + genTest("Parser", ignoreSet, test, "fail", os.list(invalidSyntaxWhile)) + + // ---------------------------------------------------- + // Testing syntatically valid programs + // ---------------------------------------------------- + + behavior of "TESTING PARSER IN valid/advanced" + genTest("Parser", ignoreSet, test, "succeed", os.list(validAdvanced)) + + behavior of "TESTING PARSER IN valid/array" + genTest("Parser", ignoreSet, test, "succeed", os.list(validArray)) + + behavior of "TESTING PARSER IN valid/basic/exit" + genTest("Parser", ignoreSet, test, "succeed", os.list(validBasicExit)) + + behavior of "TESTING PARSER IN valid/basic/skip" + genTest("Parser", ignoreSet, test, "succeed", os.list(validBasicSkip)) + + behavior of "TESTING PARSER IN valid/expressions" + genTest("Parser", ignoreSet, test, "succeed", os.list(validExpression)) + + behavior of "TESTING PARSER IN valid/function/nested_functions" + genTest("Parser", ignoreSet, test, "succeed", os.list(validFunctionNestFuns)) + + behavior of "TESTING PARSER IN valid/function/simple_functions" + genTest("Parser", ignoreSet, test, "succeed", os.list(validFunctionSimpFuns)) + + behavior of "TESTING PARSER IN valid/function/overload_functions" + genTest("Parser", ignoreSet, test, "succeed", os.list(validFunctionOverFuns)) + + behavior of "TESTING PARSER IN valid/if" + genTest("Parser", ignoreSet, test, "succeed", os.list(validIf)) + + behavior of "TESTING PARSER IN valid/IO/print" + genTest("Parser", ignoreSet, test, "succeed", os.list(validIOPrint)) + + behavior of "TESTING PARSER IN valid/IO/read" + genTest("Parser", ignoreSet, test, "succeed", os.list(validIORead)) + + behavior of "TESTING PARSER IN valid/IO/special" + genTest("Parser", ignoreSet, test, "succeed", os.list(validIOSpecial)) + + behavior of "TESTING PARSER IN valid/pairs" + genTest("Parser", ignoreSet, test, "succeed", os.list(validPair)) + + behavior of "TESTING PARSER IN valid/runtimeErr/arrayOutOfBounds" + genTest("Parser", ignoreSet, test, "succeed", os.list(validRTEArrOOB)) + + behavior of "TESTING PARSER IN valid/runtimeErr/badChar" + genTest("Parser", ignoreSet, test, "succeed", os.list(validRTEBadChar)) + + behavior of "TESTING PARSER IN valid/runtimeErr/divideByZero" + genTest("Parser", ignoreSet, test, "succeed", os.list(validRTEDivByZero)) + + behavior of "TESTING PARSER IN valid/runtimeErr/integerOverflow" + genTest("Parser", ignoreSet, test, "succeed", os.list(validRTEIntOverflow)) + + behavior of "TESTING PARSER IN valid/runtimeErr/nullDereference" + genTest("Parser", ignoreSet, test, "succeed", os.list(validRTENullDereference)) + + behavior of "TESTING PARSER IN valid/scope" + genTest("Parser", ignoreSet, test, "succeed", os.list(validScope)) + + behavior of "TESTING PARSER IN valid/sequence" + genTest("Parser", ignoreSet, test, "succeed", os.list(validSequence)) + + behavior of "TESTING PARSER IN valid/variables" + genTest("Parser", ignoreSet, test, "succeed", os.list(validVariables)) + + behavior of "TESTING PARSER IN valid/while" + genTest("Parser", ignoreSet, test, "succeed", os.list(validWhile)) + + // ---------------------------------------------------- + // Testing syntatically valid programs (These are semantically invalid) + // ---------------------------------------------------- + + behavior of "TESTING PARSER IN semanticErr/array" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticArray)) + + behavior of "TESTING PARSER IN semanticErr/exit" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticExit)) + + behavior of "TESTING PARSER IN semanticErr/expressions" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticExpressions)) + + behavior of "TESTING PARSER IN semanticErr/function" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticFunction)) + + behavior of "TESTING PARSER IN semanticErr/if" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticIf)) + + behavior of "TESTING PARSER IN semanticErr/IO" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticIO)) + + behavior of "TESTING PARSER IN semanticErr/multiple" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticMultiple)) + + behavior of "TESTING PARSER IN semanticErr/pairs" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticPairs)) + + behavior of "TESTING PARSER IN semanticErr/print" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticPrint)) + + behavior of "TESTING PARSER IN semanticErr/read" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticRead)) + + behavior of "TESTING PARSER IN semanticErr/scope" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticScope)) + + behavior of "TESTING PARSER IN semanticErr/variables" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticVariables)) + + behavior of "TESTING PARSER IN semanticErr/while" + genTest("Parser", ignoreSet, test, "succeed", os.list(invalidSemanticWhile)) +} \ No newline at end of file diff --git a/src/test/wacc/peephole.scala b/src/test/wacc/peephole.scala new file mode 100644 index 0000000..c248951 --- /dev/null +++ b/src/test/wacc/peephole.scala @@ -0,0 +1,119 @@ +import org.scalatest.flatspec.AnyFlatSpec +import wacc.extension.Peephole + +// These tests are here to ensure that all previous tests still pass after the peephole optimization is applied +class peepholeTests extends CodeGeneratorTestSuite { + + /* Wrapper for uniform testing */ + def test(input: os.Path): (Boolean, String) = { + + // Create the peephole instance + val peephole = Peephole() + super.test(input, Some(peephole), None) + } + + // IGNORE SET: Contains files/directories to ignore in testing + val ignoreSet: Set[os.Path] = Set() + + // ---------------------------------------------------- + // Testing programs with syntax errors + // ---------------------------------------------------- + + behavior of "TESTING PEEPHOLE IN syntaxErr" + + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxArray)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxBasic)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxExpressions)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxFunction)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxIf)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxLiteral)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxPairs)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxPrint)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxSequence)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxVariables)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSyntaxWhile)) + + // ---------------------------------------------------- + // Testing programs with semantic errors + // ---------------------------------------------------- + + behavior of "TESTING PEEPHOLE IN semanticErr" + + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticArray)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticExit)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticExpressions)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticFunction)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticIf)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticIO)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticMultiple)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticPairs)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticPrint)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticRead)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticScope)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticVariables)) + genTest("Peephole", ignoreSet, test, "fail", os.list(invalidSemanticWhile)) + + // ---------------------------------------------------- + // Testing valid programs + // ---------------------------------------------------- + + behavior of "TESTING PEEPHOLE IN valid/basic/exit" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validBasicExit)) + + behavior of "TESTING PEEPHOLE IN valid/basic/skip" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validBasicSkip)) + + behavior of "TESTING PEEPHOLE IN valid/expressions" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validExpression)) + + behavior of "TESTING PEEPHOLE IN valid/function/nested_functions" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validFunctionNestFuns)) + + behavior of "TESTING PEEPHOLE IN valid/function/simple_functions" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validFunctionSimpFuns)) + + behavior of "TESTING PEEPHOLE IN valid/function/overload_functions" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validFunctionOverFuns)) + + behavior of "TESTING PEEPHOLE IN valid/if" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validIf)) + + behavior of "TESTING PEEPHOLE IN valid/IO/print" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validIOPrint)) + + behavior of "TESTING PEEPHOLE IN valid/IO/read" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validIORead)) + + behavior of "TESTING PEEPHOLE IN valid/IO/special" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validIOSpecial)) + + behavior of "TESTING PEEPHOLE IN valid/pairs" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validPair)) + + behavior of "TESTING PEEPHOLE IN valid/runtimeErr/arrayOutOfBounds" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validRTEArrOOB)) + + behavior of "TESTING PEEPHOLE IN valid/runtimeErr/badChar" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validRTEBadChar)) + + behavior of "TESTING PEEPHOLE IN valid/runtimeErr/divideByZero" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validRTEDivByZero)) + + behavior of "TESTING PEEPHOLE IN valid/runtimeErr/integerOverflow" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validRTEIntOverflow)) + + behavior of "TESTING PEEPHOLE IN valid/runtimeErr/nullDereference" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validRTENullDereference)) + + behavior of "TESTING PEEPHOLE IN valid/scope" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validScope)) + + behavior of "TESTING PEEPHOLE IN valid/sequence" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validSequence)) + + behavior of "TESTING PEEPHOLE IN valid/variables" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validVariables)) + + behavior of "TESTING PEEPHOLE IN valid/while" + genTest("Peephole", ignoreSet, test, "succeed", os.list(validWhile)) +} diff --git a/src/test/wacc/semanticAnalyser.scala b/src/test/wacc/semanticAnalyser.scala new file mode 100644 index 0000000..d445442 --- /dev/null +++ b/src/test/wacc/semanticAnalyser.scala @@ -0,0 +1,143 @@ +import org.scalatest.flatspec.AnyFlatSpec +import parsley.{Success, Failure} +import wacc.frontend.syntax.ast.WProgram +import wacc.frontend.semantic.analyse + +class semanticAnalyserTests extends ParserTestSuite { + + /** + * Returns the success of parsing a wacc file + * + * @param input Path to wacc file + * @return either (true, "") on a successful analysis or (false, msg) on an unsuccessful analysis + */ + def test(input: os.Path): (Boolean, String) = { + parseFile(input) match { + case Success(x: WProgram) => { + analyse(x) match { + case Left(prog) => (true, "") + case Right(errs) => + val fileName = input.last + (false, errs.map(err => err.getMessage(fileName, input.toString)).mkString("\n")) + } + } + case Failure(msg) => (false, "OOPS. Looks like the parser failed :/") + } +} + + // IGNORE SET: Contains files/directories to ignore in testing + val ignoreSet: Set[os.Path] = Set() + + // ---------------------------------------------------- + // Testing programs with semantic errors + // ---------------------------------------------------- + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/array" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticArray)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/exit" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticExit)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/expressions" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticExpressions)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/function" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticFunction)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/if" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticIf)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/IO" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticIO)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/multiple" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticMultiple)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/pairs" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticPairs)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/print" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticPrint)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/read" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticRead)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/scope" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticScope)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/variables" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticVariables)) + + behavior of "TESTING SEMANTIC ANALYSER IN semanticErr/while" + genTest("Semantic Analyser", ignoreSet, test, "fail", os.list(invalidSemanticWhile)) + + // ---------------------------------------------------- + // Testing semantically valid programs + // ---------------------------------------------------- + + behavior of "TESTING SEMANTIC ANALYSER IN valid/advanced" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validAdvanced)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/array" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validArray)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/basic/exit" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validBasicExit)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/basic/skip" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validBasicSkip)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/expressions" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validExpression)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/function/nested_functions" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validFunctionNestFuns)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/function/simple_functions" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validFunctionSimpFuns)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/function/overload_functions" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validFunctionOverFuns)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/if" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validIf)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/IO/print" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validIOPrint)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/IO/read" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validIORead)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/IO/special" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validIOSpecial)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/pairs" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validPair)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/runtimeErr/arrayOutOfBounds" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validRTEArrOOB)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/runtimeErr/badChar" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validRTEBadChar)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/runtimeErr/divideByZero" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validRTEDivByZero)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/runtimeErr/integerOverflow" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validRTEIntOverflow)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/runtimeErr/nullDereference" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validRTENullDereference)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/scope" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validScope)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/sequence" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validSequence)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/variables" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validVariables)) + + behavior of "TESTING SEMANTIC ANALYSER IN valid/while" + genTest("Semantic Analyser", ignoreSet, test, "succeed", os.list(validWhile)) +} \ No newline at end of file diff --git a/src/test/wacc/testConfig.scala b/src/test/wacc/testConfig.scala new file mode 100644 index 0000000..a29e926 --- /dev/null +++ b/src/test/wacc/testConfig.scala @@ -0,0 +1,131 @@ +import org.scalatest.flatspec.AnyFlatSpec +import parsley.Result +import wacc.frontend.syntax.{parser, ast} +import wacc.frontend.syntax.ast._ + +// ---------------------------------------------------- +// SETUP WITH PATHS TO THE DIFFERENT DIRECTORIES +// ---------------------------------------------------- + +val basePath = os.pwd / "src" / "test" / "wacc" / "waccPrograms" +val invalidSyntaxPath = basePath / "syntaxErr" +val invalidSemanticPath = basePath / "semanticErr" +val validPath = basePath / "valid" + +// Invalid Syntax test paths +val invalidSyntaxArray = invalidSyntaxPath / "array" +val invalidSyntaxBasic = invalidSyntaxPath / "basic" +val invalidSyntaxExpressions = invalidSyntaxPath / "expressions" +val invalidSyntaxFunction = invalidSyntaxPath / "function" +val invalidSyntaxIf = invalidSyntaxPath / "if" +val invalidSyntaxLiteral = invalidSyntaxPath / "literals" +val invalidSyntaxPairs = invalidSyntaxPath / "pairs" +val invalidSyntaxPrint = invalidSyntaxPath / "print" +val invalidSyntaxSequence = invalidSyntaxPath / "sequence" +val invalidSyntaxVariables = invalidSyntaxPath / "variables" +val invalidSyntaxWhile = invalidSyntaxPath / "while" + +// Invalid Semantic test paths +val invalidSemanticArray = invalidSemanticPath / "array" +val invalidSemanticExit = invalidSemanticPath / "exit" +val invalidSemanticExpressions = invalidSemanticPath / "expressions" +val invalidSemanticFunction = invalidSemanticPath / "function" +val invalidSemanticIf = invalidSemanticPath / "if" +val invalidSemanticIO = invalidSemanticPath / "IO" +val invalidSemanticMultiple = invalidSemanticPath / "multiple" +val invalidSemanticPairs = invalidSemanticPath / "pairs" +val invalidSemanticPrint = invalidSemanticPath / "print" +val invalidSemanticRead = invalidSemanticPath / "read" +val invalidSemanticScope = invalidSemanticPath / "scope" +val invalidSemanticVariables = invalidSemanticPath / "variables" +val invalidSemanticWhile = invalidSemanticPath / "while" + +// Valid test paths +val validAdvanced = validPath / "advanced" +val validArray = validPath / "array" +val validBasicExit = validPath / "basic" / "exit" +val validBasicSkip = validPath / "basic" / "skip" +val validExpression = validPath / "expressions" +val validFunctionNestFuns = validPath / "function" / "nested_functions" +val validFunctionSimpFuns = validPath / "function" / "simple_functions" +val validFunctionOverFuns = validPath / "function" / "overload_functions" +val validFunctionImports = validPath / "function" / "imported_functions" +val validIf = validPath / "if" +val validIOSpecial = validPath / "IO" / "special" +val validIOPrint = validPath / "IO" / "print" +val validIORead = validPath / "IO" / "read" +val validPair = validPath / "pairs" +val validRTEArrOOB = validPath / "runtimeErr" / "arrayOutOfBounds" +val validRTEBadChar = validPath / "runtimeErr" / "badChar" +val validRTEDivByZero = validPath / "runtimeErr" / "divideByZero" +val validRTEIntOverflow = validPath / "runtimeErr" / "integerOverflow" +val validRTENullDereference = validPath / "runtimeErr" / "nullDereference" +val validScope = validPath / "scope" +val validSequence = validPath / "sequence" +val validVariables = validPath / "variables" +val validWhile = validPath / "while" + +// ---------------------------------------------------- +// HELPER FUNCTIONS +// ---------------------------------------------------- + +trait ParserTestSuite extends AnyFlatSpec { + + /** + * Returns parsed wacc file + * + * @param input Path to wacc file + * @return parsed wacc program + */ + def parseFile(input: os.Path): Result[String, WProgram] = { + val program = os.read(input) + parser.parse(program) + } + + /** + * Generates test cases for each file in `files` + * If the file (or directory name) appears in `dontIgnoreSet` or doesn't appear in `ignoreSet`, + * then the test will be ran. + * + * @param analyser + * @param testFunc + * @param res String indicating if the test should "fail" or "succeed" + * @param files Sequence of files within the directory of interest + */ + def genTest(analyser: String, + ignoreSet: Set[os.Path], + testFunc: (os.Path) => (Boolean, String), + res: String, + files: IndexedSeq[os.Path]): Unit = { + + for (file <- files) { + val fileName = file.last + + // -------------------------- + // IGNORING TESTS + // -------------------------- + if (ignoreSet.exists(file.startsWith)) { + ignore should s"$res with $fileName" in { + // *** test is ignored *** + } + + // -------------------------- + // TESTING REMAINING TESTS + // -------------------------- + } else { + val (verdict, msg) = testFunc(file) + val errorMessage = s"Expected $analyser to $res. $analyser returns:\n$msg" + + if (res == "fail") { + it should s"$res with $fileName" in { + assert(verdict == false, errorMessage) + } + } else { + it should s"$res with $fileName" in { + assert(verdict == true, errorMessage) + } + } + } + } + } +} \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/IO/readTypeErr.wacc b/src/test/wacc/waccPrograms/semanticErr/IO/readTypeErr.wacc new file mode 100644 index 0000000..1b9a9e6 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/IO/readTypeErr.wacc @@ -0,0 +1,15 @@ +# attempt to read into a boolean variable + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + bool b = true ; + read b ; + println b +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/arrayIndexComplexNotInt.wacc b/src/test/wacc/waccPrograms/semanticErr/array/arrayIndexComplexNotInt.wacc new file mode 100644 index 0000000..50b27a3 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/arrayIndexComplexNotInt.wacc @@ -0,0 +1,16 @@ +# Attempting to access an array with an invalid complex expression +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] a = [1, 2]; + int b = a[1 - "horse"]; + int c = a[!false] +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/array/arrayIndexNotInt.wacc b/src/test/wacc/waccPrograms/semanticErr/array/arrayIndexNotInt.wacc new file mode 100644 index 0000000..d043951 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/arrayIndexNotInt.wacc @@ -0,0 +1,15 @@ +# Attempting to access an array with a non-integer index. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] a = [1, 2]; + int b = a["horse"] +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/arrayMultipleIndexError.wacc b/src/test/wacc/waccPrograms/semanticErr/array/arrayMultipleIndexError.wacc new file mode 100644 index 0000000..bb885d0 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/arrayMultipleIndexError.wacc @@ -0,0 +1,20 @@ +# Indexing an array to get sub-arrays, but going too far. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] a = [1, 2]; + int[] b = [3, 4]; + + int[][] ab = [a, b]; + + int[] sameAsA = ab[0]; + int oops = sameAsA[0][1] +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/array/badIndex.wacc b/src/test/wacc/waccPrograms/semanticErr/array/badIndex.wacc new file mode 100644 index 0000000..0a22837 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/badIndex.wacc @@ -0,0 +1,15 @@ +# Too much indexing! +# Thanks to David Pan + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] a = [1]; + println a[1][2] +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/indexUndefIdent.wacc b/src/test/wacc/waccPrograms/semanticErr/array/indexUndefIdent.wacc new file mode 100644 index 0000000..7e9c21d --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/indexUndefIdent.wacc @@ -0,0 +1,14 @@ +# Attempting to array-index an undefined identifier. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char c = horse[2] +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/mixingTypesInArrays.wacc b/src/test/wacc/waccPrograms/semanticErr/array/mixingTypesInArrays.wacc new file mode 100644 index 0000000..7228fd3 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/mixingTypesInArrays.wacc @@ -0,0 +1,19 @@ +# Attempting to mix types in an array literal. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char five() is + begin return '5' end + end + + char f = call five(); + int[] a = [1, f] +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/noArrayCovariance.wacc b/src/test/wacc/waccPrograms/semanticErr/array/noArrayCovariance.wacc new file mode 100644 index 0000000..a543513 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/noArrayCovariance.wacc @@ -0,0 +1,15 @@ +# Arrays should not be covariant +# Thanks to Nathaniel Burke + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char[][] acs = [] ; + string[] bad = acs +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/noStringIndex.wacc b/src/test/wacc/waccPrograms/semanticErr/array/noStringIndex.wacc new file mode 100644 index 0000000..e4deb04 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/noStringIndex.wacc @@ -0,0 +1,14 @@ +# It shouldn't be possible to index strings (wrong side of the type relaxation) + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + string str = "hello world"; + char x = str[0] +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/nonMatchingArrays.wacc b/src/test/wacc/waccPrograms/semanticErr/array/nonMatchingArrays.wacc new file mode 100644 index 0000000..4b7a2fa --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/nonMatchingArrays.wacc @@ -0,0 +1,16 @@ +# Trying to assign the wrong array dimension to a variable, or non-matching pairs. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] a = [1, 2]; + int[][] aa = [a, a]; + int[] b = aa +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/wrongArrayDimension.wacc b/src/test/wacc/waccPrograms/semanticErr/array/wrongArrayDimension.wacc new file mode 100644 index 0000000..f741d3f --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/wrongArrayDimension.wacc @@ -0,0 +1,15 @@ +# Accessing too many array indices. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] a = [1, 2]; + int oops = a[1][2][3] +end diff --git a/src/test/wacc/waccPrograms/semanticErr/array/wrongArrayType.wacc b/src/test/wacc/waccPrograms/semanticErr/array/wrongArrayType.wacc new file mode 100644 index 0000000..46a152d --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/array/wrongArrayType.wacc @@ -0,0 +1,14 @@ +# Array was given wrong type. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] should_be_int_array = ['a', 'b', 'c'] +end diff --git a/src/test/wacc/waccPrograms/semanticErr/exit/badCharExit.wacc b/src/test/wacc/waccPrograms/semanticErr/exit/badCharExit.wacc new file mode 100644 index 0000000..5043a9a --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/exit/badCharExit.wacc @@ -0,0 +1,13 @@ +# tries to exit using a character + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + exit 'a' +end diff --git a/src/test/wacc/waccPrograms/semanticErr/exit/exitNonInt.wacc b/src/test/wacc/waccPrograms/semanticErr/exit/exitNonInt.wacc new file mode 100644 index 0000000..925480a --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/exit/exitNonInt.wacc @@ -0,0 +1,14 @@ +# exit with non-int - this should be an invalid program! + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char x = 'f' ; + exit x +end diff --git a/src/test/wacc/waccPrograms/semanticErr/exit/globalReturn.wacc b/src/test/wacc/waccPrograms/semanticErr/exit/globalReturn.wacc new file mode 100644 index 0000000..06c1086 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/exit/globalReturn.wacc @@ -0,0 +1,14 @@ +# trying to return from the main program + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + return 42 ; + println "should not get here" +end diff --git a/src/test/wacc/waccPrograms/semanticErr/exit/returnsInMain.wacc b/src/test/wacc/waccPrograms/semanticErr/exit/returnsInMain.wacc new file mode 100644 index 0000000..3bb0daa --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/exit/returnsInMain.wacc @@ -0,0 +1,26 @@ +# Returning from the main body is forbidden. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + while true do + return 3 + done; + + if true then + return 4 + else + return 5 + fi; + + begin + return 6 + end +end diff --git a/src/test/wacc/waccPrograms/semanticErr/expressions/boolOpTypeErr.wacc b/src/test/wacc/waccPrograms/semanticErr/expressions/boolOpTypeErr.wacc new file mode 100644 index 0000000..fa26f0d --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/expressions/boolOpTypeErr.wacc @@ -0,0 +1,13 @@ +# expresission type mismatch int->bool + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + bool b = 1 || 1 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/expressions/exprTypeErr.wacc b/src/test/wacc/waccPrograms/semanticErr/expressions/exprTypeErr.wacc new file mode 100644 index 0000000..1c5c800 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/expressions/exprTypeErr.wacc @@ -0,0 +1,13 @@ +# expresission type mismatch bool->int + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int b = 15 + 6 || 19 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/expressions/intOpTypeErr.wacc b/src/test/wacc/waccPrograms/semanticErr/expressions/intOpTypeErr.wacc new file mode 100644 index 0000000..acfd0bb --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/expressions/intOpTypeErr.wacc @@ -0,0 +1,13 @@ +# expresission type mismatch bool->int + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = true + false +end diff --git a/src/test/wacc/waccPrograms/semanticErr/expressions/lessPairExpr.wacc b/src/test/wacc/waccPrograms/semanticErr/expressions/lessPairExpr.wacc new file mode 100644 index 0000000..d47ce13 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/expressions/lessPairExpr.wacc @@ -0,0 +1,17 @@ +# evaluating less-than on references + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(int,int) x = newpair(1,2) ; + println x; + pair(int,int) y = newpair(2,3) ; + println y; + println x < y +end diff --git a/src/test/wacc/waccPrograms/semanticErr/expressions/mixedOpTypeErr.wacc b/src/test/wacc/waccPrograms/semanticErr/expressions/mixedOpTypeErr.wacc new file mode 100644 index 0000000..f89b308 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/expressions/mixedOpTypeErr.wacc @@ -0,0 +1,13 @@ +# expresission type mismatch bool->int + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int b = 1 + 2 + true + 4 + 5 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/expressions/moreArrExpr.wacc b/src/test/wacc/waccPrograms/semanticErr/expressions/moreArrExpr.wacc new file mode 100644 index 0000000..db984f9 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/expressions/moreArrExpr.wacc @@ -0,0 +1,15 @@ +# program performs boolean operations on arrays + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int[] x = [1,2]; + int[] y = [3,4]; + println x > y +end diff --git a/src/test/wacc/waccPrograms/semanticErr/expressions/stringElemErr.wacc b/src/test/wacc/waccPrograms/semanticErr/expressions/stringElemErr.wacc new file mode 100644 index 0000000..b4102e6 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/expressions/stringElemErr.wacc @@ -0,0 +1,16 @@ +# element access is not permitted for strings + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + string str = "hello world!" ; + println str ; + str[0] = 'H' ; + println str +end diff --git a/src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall.wacc b/src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall.wacc new file mode 100644 index 0000000..ee14f5d --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall.wacc @@ -0,0 +1,23 @@ +# Two overloads with different pair parameters when called with a +# `null` directly leads to ambiguity. + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(pair(int, char) x) is + return 1 + end + + int f(pair(int, bool) x) is + return 2 + end + + int result = call f(null) ; + println result +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall2.wacc b/src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall2.wacc new file mode 100644 index 0000000..cc2852b --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/ambiguousCall2.wacc @@ -0,0 +1,25 @@ +# Overloads "fun" in a way that calling with a char array +# Could match multiple signatures (char[] or string) + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int fun(string x) is + int y = 1; + return y + end + + int fun(char[] x) is + return len x + end + + char[] text = ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] ; + int result = call fun(text) ; + println result +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/function/ambiguousOverload.wacc b/src/test/wacc/waccPrograms/semanticErr/function/ambiguousOverload.wacc new file mode 100644 index 0000000..e1f9c30 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/ambiguousOverload.wacc @@ -0,0 +1,27 @@ +# Two overloads with identical parameter lists but different return types +# leads to ambiguity. + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int x) is + return x + end + + bool f(int x) is + return x > 0 + end + + bool result = call f(10) ; + if result then + println "Positive" + else + println "Non-positive" + fi +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/function/callHasNoMatchingOverload.wacc b/src/test/wacc/waccPrograms/semanticErr/function/callHasNoMatchingOverload.wacc new file mode 100644 index 0000000..78fcc12 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/callHasNoMatchingOverload.wacc @@ -0,0 +1,24 @@ +# Defines overloaded "f" for int and bool, but calls with a char. +# There's no matching overload that takes a char. + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int x) is + return x + 1 + end + + bool f(bool x) is + return !x + end + + # Attempt to call f with a char argument -> no matching overload + int test = call f('z') ; + println test +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/function/callUndefFunction.wacc b/src/test/wacc/waccPrograms/semanticErr/function/callUndefFunction.wacc new file mode 100644 index 0000000..97bf30c --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/callUndefFunction.wacc @@ -0,0 +1,14 @@ +# Calling an undefined identifier. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int result = call fib(5) +end diff --git a/src/test/wacc/waccPrograms/semanticErr/function/doubleArgDef.wacc b/src/test/wacc/waccPrograms/semanticErr/function/doubleArgDef.wacc new file mode 100644 index 0000000..40e4bf3 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/doubleArgDef.wacc @@ -0,0 +1,17 @@ +# functions cannot define the same argument twice + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int foo(int x, int x) is + return x + end + + skip +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/function/funcVarAccess.wacc b/src/test/wacc/waccPrograms/semanticErr/function/funcVarAccess.wacc new file mode 100644 index 0000000..202356f --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/funcVarAccess.wacc @@ -0,0 +1,20 @@ +# functions cannot access global variables + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + x = -1 ; + return 0 + end + int x = 5 ; + int y = call f() ; + println x +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionAssign.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionAssign.wacc new file mode 100644 index 0000000..e2ab8be --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionAssign.wacc @@ -0,0 +1,17 @@ +# tries to assign to a function + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + return 3 + end + + f = 2 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadArgUse.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadArgUse.wacc new file mode 100644 index 0000000..a137d32 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadArgUse.wacc @@ -0,0 +1,18 @@ +# function parameter misuse + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int x) is + bool b = x && true ; + return 0 + end + int x = call f(0) +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadCall.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadCall.wacc new file mode 100644 index 0000000..4a3001a --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadCall.wacc @@ -0,0 +1,18 @@ +# function call type mismatch + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + return 0 + end + bool b = call f() ; + println b +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadCall2.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadCall2.wacc new file mode 100644 index 0000000..9a3a4db --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadCall2.wacc @@ -0,0 +1,23 @@ +# function call type mismatch for both functions + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + return 0 + end + + bool f() is + return false + end + + string s = call f() ; + println s +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadParam.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadParam.wacc new file mode 100644 index 0000000..c7e00d5 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadParam.wacc @@ -0,0 +1,18 @@ +# function parameter type mismatch + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int x) is + return x + end + bool b = true ; + int x = call f(b) +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadParam2.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadParam2.wacc new file mode 100644 index 0000000..2206aa2 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadParam2.wacc @@ -0,0 +1,24 @@ +# function parameter type mismatch for either function + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(pair(int, int) x) is + int y = fst x ; + return y + end + + char f(int x) is + return chr x + end + + bool b = true ; + int x = call f(b) +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn.wacc new file mode 100644 index 0000000..8df9065 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn.wacc @@ -0,0 +1,18 @@ +# function return type mismatch: int <- char + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + return 'c' + end + int x = call f() ; + println x +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn2.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn2.wacc new file mode 100644 index 0000000..18132b6 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn2.wacc @@ -0,0 +1,23 @@ +# function return type mismatch: int <- char even though there's an overloaded function with the correct return + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char f() is + return 'c' + end + + int f() is + return 'c' + end + + int x = call f() ; + println x +end + \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn3.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn3.wacc new file mode 100644 index 0000000..be30e6a --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionBadReturn3.wacc @@ -0,0 +1,23 @@ +# function return type mismatch: char <- int even though there's an overloaded function with the correct return + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char f() is + return 1 + end + + int f() is + return 'a' + end + + char x = call f() ; + println x +end + \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs.wacc new file mode 100644 index 0000000..93569cb --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs.wacc @@ -0,0 +1,18 @@ +# function call with too many arguments + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int a, int b) is + return 0 + end + int x = call f(1,2,3); + println x +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs2.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs2.wacc new file mode 100644 index 0000000..26c1078 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionOverArgs2.wacc @@ -0,0 +1,23 @@ +# function call with more arguments than either function + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int a) is + return 0 + end + + int f(int a, int b) is + return 0 + end + + int x = call f(1,2,3); + println x +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionRedefine.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionRedefine.wacc new file mode 100644 index 0000000..b811598 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionRedefine.wacc @@ -0,0 +1,20 @@ +# attempted redefinition of function of the same signature + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + return 0 + end + int f() is + return 1 + end + int x = call f(); + println x +end diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionRedefine2.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionRedefine2.wacc new file mode 100644 index 0000000..910fcb4 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionRedefine2.wacc @@ -0,0 +1,20 @@ +# attempted redefinition of function of the same signature + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int a, string b, char c, bool d, pair(int, int) e, int[] f) is + return 0 + end + int f(int a, string b, char c, bool d, pair(int, int) e, int[] f) is + return 1 + end + + skip +end diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionSwapArgs.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionSwapArgs.wacc new file mode 100644 index 0000000..a3c1f07 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionSwapArgs.wacc @@ -0,0 +1,19 @@ +# function call with arguments swapped + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int a, bool b) is + return 0 + end + int x = call f(true,1); + println x +end + + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/functionUnderArgs.wacc b/src/test/wacc/waccPrograms/semanticErr/function/functionUnderArgs.wacc new file mode 100644 index 0000000..840e862 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/functionUnderArgs.wacc @@ -0,0 +1,19 @@ +# function call with too few arguments + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(int a, int b) is + return 0 + end + int x = call f(1); + println x +end + + diff --git a/src/test/wacc/waccPrograms/semanticErr/function/invalidReturnsBranched.wacc b/src/test/wacc/waccPrograms/semanticErr/function/invalidReturnsBranched.wacc new file mode 100644 index 0000000..d940c9b --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/invalidReturnsBranched.wacc @@ -0,0 +1,22 @@ +# Trying to return something invalid from a branch. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + if true then + return horse + else + return 1 + fi + end + + int result = call f() +end diff --git a/src/test/wacc/waccPrograms/semanticErr/function/mismatchingReturns.wacc b/src/test/wacc/waccPrograms/semanticErr/function/mismatchingReturns.wacc new file mode 100644 index 0000000..e49d079 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/function/mismatchingReturns.wacc @@ -0,0 +1,27 @@ +# Trying to return two different types from a function. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(bool b) is + if b then + return 'c'; + while true do + return 'd' + done + else + skip + fi; + + return 10 + end + + int a = call f(false) +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/if/ifIntCondition.wacc b/src/test/wacc/waccPrograms/semanticErr/if/ifIntCondition.wacc new file mode 100644 index 0000000..b0af61c --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/if/ifIntCondition.wacc @@ -0,0 +1,17 @@ +# if condition type mismatch + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + if 15 + 6 then + skip + else + skip + fi +end diff --git a/src/test/wacc/waccPrograms/semanticErr/multiple/funcMess.wacc b/src/test/wacc/waccPrograms/semanticErr/multiple/funcMess.wacc new file mode 100644 index 0000000..78cab69 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/multiple/funcMess.wacc @@ -0,0 +1,25 @@ +# a complete mess of function definition and use + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f(bool b) is + x = -1 ; + int x = 1; + bool x = 3; + return "done" + end + + int x = 5 ; + int y = call f(x) ; + println y ; + println x + +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/multiple/ifAndWhileErrs.wacc b/src/test/wacc/waccPrograms/semanticErr/multiple/ifAndWhileErrs.wacc new file mode 100644 index 0000000..c0a7f11 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/multiple/ifAndWhileErrs.wacc @@ -0,0 +1,19 @@ +# boolean typo and if condition type mismatch + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + while tru do + if 15 + 6 then + skip + else + skip + fi + done +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/multiple/messyExpr.wacc b/src/test/wacc/waccPrograms/semanticErr/multiple/messyExpr.wacc new file mode 100644 index 0000000..c73dd60 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/multiple/messyExpr.wacc @@ -0,0 +1,16 @@ +# long expression with multiple type mismatches + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + + int x = ((1 + true) * (2 - false)) || 17 ; + exit x + +end diff --git a/src/test/wacc/waccPrograms/semanticErr/multiple/multiCaseSensitivity.wacc b/src/test/wacc/waccPrograms/semanticErr/multiple/multiCaseSensitivity.wacc new file mode 100644 index 0000000..0282401 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/multiple/multiCaseSensitivity.wacc @@ -0,0 +1,20 @@ +# variable names are case sensitive in all possible ways + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int number = 42 ; + println NUMBER ; + + int INDEX = 0 ; + println index ; + + int miXed = 3 ; + println MIxED +end diff --git a/src/test/wacc/waccPrograms/semanticErr/multiple/multiTypeErrs.wacc b/src/test/wacc/waccPrograms/semanticErr/multiple/multiTypeErrs.wacc new file mode 100644 index 0000000..1c7b84c --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/multiple/multiTypeErrs.wacc @@ -0,0 +1,15 @@ +# multiple type mismatches: int <- bool, bool <- char, char <- int + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int i = true ; + bool b = 'a' ; + char c = 10 +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/multiple/obfuscatingReturnsWithWhile.wacc b/src/test/wacc/waccPrograms/semanticErr/multiple/obfuscatingReturnsWithWhile.wacc new file mode 100644 index 0000000..fd96c66 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/multiple/obfuscatingReturnsWithWhile.wacc @@ -0,0 +1,44 @@ +# Trying to obfuscate invalid returns with whiles. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int f() is + if true then + while true do + return 'a' + done + else + return 5 + fi; + if false then + return 2 + else + while false do + return 'b' + done + fi; + if true then + while true do + return -2 + done + else + while false do + return -4 + done + fi; + while false do + return !"horse" + done; + exit -1 + end + + int i = call f() +end diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/badPairAssign.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/badPairAssign.wacc new file mode 100644 index 0000000..c44b41c --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/badPairAssign.wacc @@ -0,0 +1,13 @@ +# newpair cannot be assigned to non-pair type + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = newpair(10, 20) +end diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/badPairExchange.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/badPairExchange.wacc new file mode 100644 index 0000000..9c85e3d --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/badPairExchange.wacc @@ -0,0 +1,15 @@ +# Assignment is not legal when both sides types are not known + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(int, int) p = newpair(4, 5); + pair(pair, int) q = newpair(p, 6); + fst fst q = snd fst q +end diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/freeNonPair.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/freeNonPair.wacc new file mode 100644 index 0000000..a42079b --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/freeNonPair.wacc @@ -0,0 +1,16 @@ +# call free on a non-pair type + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = 5; + println x; + free 9 ; + println x +end diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/mismatchedPair.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/mismatchedPair.wacc new file mode 100644 index 0000000..effe8f3 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/mismatchedPair.wacc @@ -0,0 +1,13 @@ +# newpair must match the underlying type of the pair + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(char, bool) x = newpair(10, 20) +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/noPairCovariance.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/noPairCovariance.wacc new file mode 100644 index 0000000..c6adf4f --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/noPairCovariance.wacc @@ -0,0 +1,15 @@ +# Pairs should not be covariant +# Thanks to Nathaniel Burke + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(char[], char[]) pcs = null ; + pair(string, string) bad = pcs +end diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/nonMatchingPairs.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/nonMatchingPairs.wacc new file mode 100644 index 0000000..981e927 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/nonMatchingPairs.wacc @@ -0,0 +1,16 @@ +# Trying to assign non-matching pairs to eachother. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(int, int) p1 = newpair(0, 0); + pair(char, char) p2 = newpair('a', 'a'); + p2 = p1 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/readUnknown.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/readUnknown.wacc new file mode 100644 index 0000000..7e44d52 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/readUnknown.wacc @@ -0,0 +1,14 @@ +# Reading is not legal when the type is not known + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(pair, int) p = null; + read fst fst p +end diff --git a/src/test/wacc/waccPrograms/semanticErr/pairs/wrongTypeInParameterlessPair.wacc b/src/test/wacc/waccPrograms/semanticErr/pairs/wrongTypeInParameterlessPair.wacc new file mode 100644 index 0000000..eb53cd2 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/pairs/wrongTypeInParameterlessPair.wacc @@ -0,0 +1,18 @@ +# Trying to place an incorrect type into a parameterless pair. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int c = 5; + pair(int, int) p = newpair(c, c); + pair(pair, int) oops = newpair(p, 0); + + fst oops = c +end diff --git a/src/test/wacc/waccPrograms/semanticErr/print/printTypeErr01.wacc b/src/test/wacc/waccPrograms/semanticErr/print/printTypeErr01.wacc new file mode 100644 index 0000000..b83e207 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/print/printTypeErr01.wacc @@ -0,0 +1,15 @@ +# type mismatch: int <- char + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = 4 ; + char y = 'a' ; + println x + y +end diff --git a/src/test/wacc/waccPrograms/semanticErr/read/readIntoBadFst.wacc b/src/test/wacc/waccPrograms/semanticErr/read/readIntoBadFst.wacc new file mode 100644 index 0000000..f3737ea --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/read/readIntoBadFst.wacc @@ -0,0 +1,14 @@ +# If we are reading into a pair projection, the element must be readable + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(bool, bool) p = null; + read fst p +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/read/readIntoBadSnd.wacc b/src/test/wacc/waccPrograms/semanticErr/read/readIntoBadSnd.wacc new file mode 100644 index 0000000..98ad9ae --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/read/readIntoBadSnd.wacc @@ -0,0 +1,14 @@ +# If we are reading into a pair projection, the element must be readable + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(bool, bool) p = null; + read snd p +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/read/readTypeErr01.wacc b/src/test/wacc/waccPrograms/semanticErr/read/readTypeErr01.wacc new file mode 100644 index 0000000..c3d37ff --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/read/readTypeErr01.wacc @@ -0,0 +1,15 @@ +# read into pair type + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + pair(int, int) p = newpair(1,2); + read p +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/scope/badParentScope.wacc b/src/test/wacc/waccPrograms/semanticErr/scope/badParentScope.wacc new file mode 100644 index 0000000..fe2326c --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/scope/badParentScope.wacc @@ -0,0 +1,20 @@ +# Stuff that follows a child scope is not part of the parent! +# Thanks to Ollie Madine and Andrii Verveha + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = 5; + begin + begin + free x + end; + int[] x = [1] + end +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/semanticErr/scope/badScopeRedefine.wacc b/src/test/wacc/waccPrograms/semanticErr/scope/badScopeRedefine.wacc new file mode 100644 index 0000000..57cdbbe --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/scope/badScopeRedefine.wacc @@ -0,0 +1,19 @@ +# variable scoping test that uses a redefined variable incorrectly + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = 12 ; + begin + bool x = true ; + x = 5 + end ; + exit x +end + diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr01.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr01.wacc new file mode 100644 index 0000000..6c4c7d4 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr01.wacc @@ -0,0 +1,13 @@ +# type mismatch: int <- bool + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int i = true +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr02.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr02.wacc new file mode 100644 index 0000000..76dc01c --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr02.wacc @@ -0,0 +1,13 @@ +# type mismatch: int <- char + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int i = 'f' +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr03.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr03.wacc new file mode 100644 index 0000000..ff58a99 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr03.wacc @@ -0,0 +1,13 @@ +# type mismatch: int <- string + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int i = "foo" +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr04.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr04.wacc new file mode 100644 index 0000000..fa99187 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr04.wacc @@ -0,0 +1,13 @@ +# type mismatch: bool <- int + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + bool b = 1 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr05.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr05.wacc new file mode 100644 index 0000000..1987f95 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr05.wacc @@ -0,0 +1,13 @@ +# type mismatch: bool <- char + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + bool b = 't' +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr06.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr06.wacc new file mode 100644 index 0000000..c232c9d --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr06.wacc @@ -0,0 +1,13 @@ +# type mismatch: bool <- string + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + bool b = "true" +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr07.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr07.wacc new file mode 100644 index 0000000..ff95ddd --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr07.wacc @@ -0,0 +1,13 @@ +# type mismatch: char <- int + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char c = 1 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr08.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr08.wacc new file mode 100644 index 0000000..d4b2915 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr08.wacc @@ -0,0 +1,13 @@ +# type mismatch: char <- bool + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char c = false +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr09.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr09.wacc new file mode 100644 index 0000000..8dc56b9 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr09.wacc @@ -0,0 +1,13 @@ +# type mismatch: char <- string + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + char c = "a" +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr10.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr10.wacc new file mode 100644 index 0000000..1d46115 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr10.wacc @@ -0,0 +1,13 @@ +# type mismatch: string <- int + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + string s = 128 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr11.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr11.wacc new file mode 100644 index 0000000..757d4a9 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr11.wacc @@ -0,0 +1,13 @@ +# type mismatch: string <- bool + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + string s = true +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr12.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr12.wacc new file mode 100644 index 0000000..15c8728 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/basicTypeErr12.wacc @@ -0,0 +1,13 @@ +# type mismatch: string <- char + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + string s = 'h' +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/caseMatters.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/caseMatters.wacc new file mode 100644 index 0000000..55b391f --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/caseMatters.wacc @@ -0,0 +1,16 @@ +# variable names are case sensitive + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + + int number = 42 ; + println NUMBER + +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/doubleDeclare.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/doubleDeclare.wacc new file mode 100644 index 0000000..a2d65aa --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/doubleDeclare.wacc @@ -0,0 +1,14 @@ +# redeclaration of variable + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = 10 ; + int x = 20 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredScopeVar.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredScopeVar.wacc new file mode 100644 index 0000000..e0c8505 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredScopeVar.wacc @@ -0,0 +1,16 @@ +# assignment to variable not declared in current scope + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + begin + int x = 5 + end ; + x = 10 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVar.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVar.wacc new file mode 100644 index 0000000..6a6ea50 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVar.wacc @@ -0,0 +1,13 @@ +# assignment to undeclared variable + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + x = 10 +end diff --git a/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVarAccess.wacc b/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVarAccess.wacc new file mode 100644 index 0000000..0d03f83 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/variables/undeclaredVarAccess.wacc @@ -0,0 +1,14 @@ +# access attempt on undeclared variable + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + int x = 10 ; + x = x + y +end diff --git a/src/test/wacc/waccPrograms/semanticErr/while/falsErr.wacc b/src/test/wacc/waccPrograms/semanticErr/while/falsErr.wacc new file mode 100644 index 0000000..8ba8043 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/while/falsErr.wacc @@ -0,0 +1,15 @@ +# false typo + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + while fals do + skip + done +end diff --git a/src/test/wacc/waccPrograms/semanticErr/while/truErr.wacc b/src/test/wacc/waccPrograms/semanticErr/while/truErr.wacc new file mode 100644 index 0000000..54260c4 --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/while/truErr.wacc @@ -0,0 +1,15 @@ +# true typo + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + while tru do + skip + done +end diff --git a/src/test/wacc/waccPrograms/semanticErr/while/whileIntCondition.wacc b/src/test/wacc/waccPrograms/semanticErr/while/whileIntCondition.wacc new file mode 100644 index 0000000..dca844a --- /dev/null +++ b/src/test/wacc/waccPrograms/semanticErr/while/whileIntCondition.wacc @@ -0,0 +1,15 @@ +# while condition type mismatch + +# Output: +# #semantic_error# + +# Exit: +# 200 + +# Program: + +begin + while 15 + 6 do + skip + done +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/array/arrayExpr.wacc b/src/test/wacc/waccPrograms/syntaxErr/array/arrayExpr.wacc new file mode 100644 index 0000000..6a43bc8 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/array/arrayExpr.wacc @@ -0,0 +1,14 @@ +# array literals are not first-class expressions + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int[] b = [1, 2, 3] ++ [4] ; + println b[3] +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/badComment.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/badComment.wacc new file mode 100644 index 0000000..62813d6 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/badComment.wacc @@ -0,0 +1,14 @@ +# program has missing comment declaration + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + oops, this is supposed to be a comment + print "Hello World!\n" +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/badComment2.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/badComment2.wacc new file mode 100644 index 0000000..42c02e4 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/badComment2.wacc @@ -0,0 +1,15 @@ +# program has missing in-line comment declaration + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int x = 10 ; can I put in-line comments after the ';'? + x = 20 ; #yes, but only if you remember to use the '#' charcter! + exit x +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/badEscape.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/badEscape.wacc new file mode 100644 index 0000000..60b76d3 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/badEscape.wacc @@ -0,0 +1,13 @@ +# program has unacceptable escape character + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + char a = '\H' +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/beginNoend.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/beginNoend.wacc new file mode 100644 index 0000000..49d6002 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/beginNoend.wacc @@ -0,0 +1,11 @@ +# begin missing closing end + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin skip diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/bgnErr.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/bgnErr.wacc new file mode 100644 index 0000000..7cd5241 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/bgnErr.wacc @@ -0,0 +1,11 @@ +# begin token typo + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +bgn skip end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/multipleBegins.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/multipleBegins.wacc new file mode 100644 index 0000000..5f95ce3 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/multipleBegins.wacc @@ -0,0 +1,17 @@ +# begin missing closing end + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + skip +end + +begin + skip +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/noBody.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/noBody.wacc new file mode 100644 index 0000000..8e277d9 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/noBody.wacc @@ -0,0 +1,11 @@ +# program missing body + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/skpErr.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/skpErr.wacc new file mode 100644 index 0000000..1249ba0 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/skpErr.wacc @@ -0,0 +1,11 @@ +# skip typo + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin skp end diff --git a/src/test/wacc/waccPrograms/syntaxErr/basic/unescapedChar.wacc b/src/test/wacc/waccPrograms/syntaxErr/basic/unescapedChar.wacc new file mode 100644 index 0000000..e4ebbca --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/basic/unescapedChar.wacc @@ -0,0 +1,13 @@ +# unescaped double quote + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + char a = '"' +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand1.wacc b/src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand1.wacc new file mode 100644 index 0000000..db7063d --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand1.wacc @@ -0,0 +1,13 @@ +# operator missing first operand + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int b = * 6 +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand2.wacc b/src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand2.wacc new file mode 100644 index 0000000..f562e1c --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/expressions/missingOperand2.wacc @@ -0,0 +1,13 @@ +# operator missing second operand + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int b = 2 - +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/expressions/printlnConcat.wacc b/src/test/wacc/waccPrograms/syntaxErr/expressions/printlnConcat.wacc new file mode 100644 index 0000000..858da0f --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/expressions/printlnConcat.wacc @@ -0,0 +1,13 @@ +# string concatenation is not part of the language + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + println "Hello " ++ "World!" +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/badlyNamed.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/badlyNamed.wacc new file mode 100644 index 0000000..6ae1d2a --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/badlyNamed.wacc @@ -0,0 +1,17 @@ +# function return type and name are conflated + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + intf() is + return 42 + end + + int x = call f() +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/badlyPlaced.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/badlyPlaced.wacc new file mode 100644 index 0000000..22afd36 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/badlyPlaced.wacc @@ -0,0 +1,17 @@ +# program has function before begin + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +int foo() is + return 42 +end + +begin + int x = call foo() +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/funcExpr.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/funcExpr.wacc new file mode 100644 index 0000000..747f8ca --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/funcExpr.wacc @@ -0,0 +1,17 @@ +# function calls are not first-class expressions + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f() is + return 0 + end + int x = f() + f() +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/funcExpr2.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/funcExpr2.wacc new file mode 100644 index 0000000..3d6539b --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/funcExpr2.wacc @@ -0,0 +1,23 @@ +# trying to call a function in a general expression + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f (int x) is + return 0 + end + println "init x" ; + int x = 2 ; + while x > f(x) do + println x ; + x = x - 1 + done; + println "finshed I guess" +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionConditionalNoReturn.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionConditionalNoReturn.wacc new file mode 100644 index 0000000..4e02a0c --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionConditionalNoReturn.wacc @@ -0,0 +1,27 @@ +# function body missing return or exit statement on a path +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + bool f ( bool b ) is + if ( b ) then + return b + else + skip + fi + end + bool g ( bool b ) is + if ( b ) then + skip + else + return b + fi + end + bool c = call f ( false ) ; + bool d = call g ( true ) +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionEndingNotReturn.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionEndingNotReturn.wacc new file mode 100644 index 0000000..43fd978 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionEndingNotReturn.wacc @@ -0,0 +1,19 @@ +# function body not terminated with return or exit statement +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f() is + return 2; + println "How on Earth did we get here?" + end + + int x = call f(); + println x +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionLateDefine.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionLateDefine.wacc new file mode 100644 index 0000000..e1e60f4 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionLateDefine.wacc @@ -0,0 +1,23 @@ +# attempted function definition after body begun + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f() is + return 0 + end + int x = call f(); + println x ; + int g() is + return 1 + end + int y = call g(); + println y +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingCall.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingCall.wacc new file mode 100644 index 0000000..eb9bd84 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingCall.wacc @@ -0,0 +1,17 @@ +# program missing call token at function use + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f() is + return 0 + end + int x = f() +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingPType.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingPType.wacc new file mode 100644 index 0000000..58bcf74 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingPType.wacc @@ -0,0 +1,16 @@ +# program missing parameter type in function + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f(x) is + return 0 + end + int x = call f(1) +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingParam.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingParam.wacc new file mode 100644 index 0000000..bc254a0 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingParam.wacc @@ -0,0 +1,17 @@ +# program extra missing parameter in function definition + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f(int x, int y,) is + return 0 + end + int x = call f(1,2,3) +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingType.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingType.wacc new file mode 100644 index 0000000..6068e6b --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionMissingType.wacc @@ -0,0 +1,17 @@ +# program missing return type of function + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + f() is + return 0 + end + int x = call f() +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionNoReturn.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionNoReturn.wacc new file mode 100644 index 0000000..d71d153 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionNoReturn.wacc @@ -0,0 +1,18 @@ +# function body missing return or exit statement +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f() is + bool b = true + end + + int x = call f(); + println x +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionReturnInLoop.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionReturnInLoop.wacc new file mode 100644 index 0000000..1e31d2a --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionReturnInLoop.wacc @@ -0,0 +1,26 @@ +# function body missing a guaranteed return (the loop might not be entered) + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f(int n) is + int i = 0; + while i < n do + i = i + 1; + if i >= n + then + return i + else + skip + fi + done + end + int x = call f(10) ; + println x +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/functionScopeDef.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/functionScopeDef.wacc new file mode 100644 index 0000000..b6da394 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/functionScopeDef.wacc @@ -0,0 +1,21 @@ +# attempted function definition in new scope + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + begin + int f() is + return 0 + end + + int x = call f(); + println x + end +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/mutualRecursionNoReturn.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/mutualRecursionNoReturn.wacc new file mode 100644 index 0000000..ee08e8e --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/mutualRecursionNoReturn.wacc @@ -0,0 +1,31 @@ +# function body of r2 missing return +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int r1(int x) is + if x == 0 + then + skip + else + print "r1: sending " ; + println x ; + int y = call r2(x) + fi ; + return 42 + end + + int r2(int y) is + print "r2: received " ; + println y ; + int z = call r1(y - 1) + end + + int x = 0 ; + x = call r1(8) +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/noBodyAfterFuncs.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/noBodyAfterFuncs.wacc new file mode 100644 index 0000000..b29a6a7 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/noBodyAfterFuncs.wacc @@ -0,0 +1,16 @@ +# program missing body after function declaration + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f() is + return 0 + end +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/function/thisIsNotC.wacc b/src/test/wacc/waccPrograms/syntaxErr/function/thisIsNotC.wacc new file mode 100644 index 0000000..b969e06 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/function/thisIsNotC.wacc @@ -0,0 +1,22 @@ +# Uses C-style pointers. This is WACC, not C. + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int f(int *x) is + *x = *x + 1; + return 0 + end + + int *x = malloc(4); + x[0] = 77; + int f = call f(x); + println *(x); + free x +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/if/ifNoelse.wacc b/src/test/wacc/waccPrograms/syntaxErr/if/ifNoelse.wacc new file mode 100644 index 0000000..d05bd2d --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/if/ifNoelse.wacc @@ -0,0 +1,16 @@ +# if missing else clause + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + if true + then + skip + fi +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/if/ifNofi.wacc b/src/test/wacc/waccPrograms/syntaxErr/if/ifNofi.wacc new file mode 100644 index 0000000..838d90e --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/if/ifNofi.wacc @@ -0,0 +1,17 @@ +# if missing closing fi + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + if true + then + skip + else + skip +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/if/ifNothen.wacc b/src/test/wacc/waccPrograms/syntaxErr/if/ifNothen.wacc new file mode 100644 index 0000000..c6d163e --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/if/ifNothen.wacc @@ -0,0 +1,17 @@ +# if missing then clause + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + if true + else + skip + fi +end + diff --git a/src/test/wacc/waccPrograms/syntaxErr/if/ifiErr.wacc b/src/test/wacc/waccPrograms/syntaxErr/if/ifiErr.wacc new file mode 100644 index 0000000..a2fe632 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/if/ifiErr.wacc @@ -0,0 +1,18 @@ +# if typo + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + ifi true + then + skip + else + skip + fi +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/literals/charLiteralSingle.wacc b/src/test/wacc/waccPrograms/syntaxErr/literals/charLiteralSingle.wacc new file mode 100644 index 0000000..43d0740 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/literals/charLiteralSingle.wacc @@ -0,0 +1,14 @@ +# Characters can have at most one character in them! +# Thanks to Panayiotis Gavriil + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + char c = 'Hello Jamie' +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralNoNewlines.wacc b/src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralNoNewlines.wacc new file mode 100644 index 0000000..0065e17 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralNoNewlines.wacc @@ -0,0 +1,14 @@ +# Strings cannot be split across lines + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + string s = "hello + world" +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralOnlyAscii.wacc b/src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralOnlyAscii.wacc new file mode 100644 index 0000000..84775fd --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/literals/stringLiteralOnlyAscii.wacc @@ -0,0 +1,13 @@ +# Strings must only contain ASCII + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + string s = "héllo world" +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup01.wacc b/src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup01.wacc new file mode 100644 index 0000000..c01e9e4 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup01.wacc @@ -0,0 +1,14 @@ +# try to directly print pair's first element + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') ; + println fst p +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup02.wacc b/src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup02.wacc new file mode 100644 index 0000000..0e5bb7f --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/pairs/badLookup02.wacc @@ -0,0 +1,14 @@ +# try to directly print pair's second element + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') ; + println snd p +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/pairs/elemOfNonPair.wacc b/src/test/wacc/waccPrograms/syntaxErr/pairs/elemOfNonPair.wacc new file mode 100644 index 0000000..1afe177 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/pairs/elemOfNonPair.wacc @@ -0,0 +1,16 @@ +# Trying to get the fst or snd of a non-pair. +# Thanks to Ethan Range, Fawwaz Abdullah, Robbie Buxton, and Edward Hartley + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int a = 5; + int b = fst a; + int c = snd (5 + 'a') +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/pairs/fstNull.wacc b/src/test/wacc/waccPrograms/syntaxErr/pairs/fstNull.wacc new file mode 100644 index 0000000..b1e45ba --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/pairs/fstNull.wacc @@ -0,0 +1,14 @@ +# call fst on a null pair literal + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int i = fst null; + println i +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/pairs/noNesting.wacc b/src/test/wacc/waccPrograms/syntaxErr/pairs/noNesting.wacc new file mode 100644 index 0000000..00fef2c --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/pairs/noNesting.wacc @@ -0,0 +1,13 @@ +# pair types cannot be nested + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + pair(pair(int, string), char) p = null +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/pairs/sndNull.wacc b/src/test/wacc/waccPrograms/syntaxErr/pairs/sndNull.wacc new file mode 100644 index 0000000..98781db --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/pairs/sndNull.wacc @@ -0,0 +1,14 @@ +# call snd on a null pair literal + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int i = snd null; + println i +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/print/printlnCharArry.wacc b/src/test/wacc/waccPrograms/syntaxErr/print/printlnCharArry.wacc new file mode 100644 index 0000000..5241c89 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/print/printlnCharArry.wacc @@ -0,0 +1,13 @@ +# You cannot directly print a char[] in WACC + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + println ['H','e','l','l','o',' ','W','o','r','l','d','!'] +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/sequence/doubleSeq.wacc b/src/test/wacc/waccPrograms/syntaxErr/sequence/doubleSeq.wacc new file mode 100644 index 0000000..932bc39 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/sequence/doubleSeq.wacc @@ -0,0 +1,14 @@ +# missing sequential composition + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + skip;; + skip +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/sequence/emptySeq.wacc b/src/test/wacc/waccPrograms/syntaxErr/sequence/emptySeq.wacc new file mode 100644 index 0000000..9f73777 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/sequence/emptySeq.wacc @@ -0,0 +1,11 @@ +# extra sequential composition + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin ; end diff --git a/src/test/wacc/waccPrograms/syntaxErr/sequence/endSeq.wacc b/src/test/wacc/waccPrograms/syntaxErr/sequence/endSeq.wacc new file mode 100644 index 0000000..42b3bac --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/sequence/endSeq.wacc @@ -0,0 +1,11 @@ +# extra sequential composition + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin skip end; diff --git a/src/test/wacc/waccPrograms/syntaxErr/sequence/extraSeq.wacc b/src/test/wacc/waccPrograms/syntaxErr/sequence/extraSeq.wacc new file mode 100644 index 0000000..a8ce132 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/sequence/extraSeq.wacc @@ -0,0 +1,11 @@ +# extra sequential composition + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin skip; end diff --git a/src/test/wacc/waccPrograms/syntaxErr/sequence/missingSeq.wacc b/src/test/wacc/waccPrograms/syntaxErr/sequence/missingSeq.wacc new file mode 100644 index 0000000..c6969c5 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/sequence/missingSeq.wacc @@ -0,0 +1,14 @@ +# missing sequential composition + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + skip + skip +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments.wacc b/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments.wacc new file mode 100644 index 0000000..fb55312 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments.wacc @@ -0,0 +1,14 @@ +# bad integer assignments - multiple syntax errors + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int x = 22 0 ; + int y = 1A4 +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments1.wacc b/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments1.wacc new file mode 100644 index 0000000..a27d094 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments1.wacc @@ -0,0 +1,13 @@ +# bad integer assignment - whitespace should delimit ints + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int x = 22 0 +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments2.wacc b/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments2.wacc new file mode 100644 index 0000000..efb030e --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/variables/badintAssignments2.wacc @@ -0,0 +1,13 @@ +# bad integer assignments - malformed int literal + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int y = 1A4 +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/variables/bigIntAssignment.wacc b/src/test/wacc/waccPrograms/syntaxErr/variables/bigIntAssignment.wacc new file mode 100644 index 0000000..2f877ff --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/variables/bigIntAssignment.wacc @@ -0,0 +1,13 @@ +# int assignment overlow + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + int x = 2200000000 +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/variables/varNoName.wacc b/src/test/wacc/waccPrograms/syntaxErr/variables/varNoName.wacc new file mode 100644 index 0000000..da6345c --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/variables/varNoName.wacc @@ -0,0 +1,14 @@ +# variable declaration missing variable name + +# Output: +# #syntax_error# + +# Exit: +# 100 + + +# Program: + +begin + int = 42 +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/while/donoErr.wacc b/src/test/wacc/waccPrograms/syntaxErr/while/donoErr.wacc new file mode 100644 index 0000000..36b2b6b --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/while/donoErr.wacc @@ -0,0 +1,15 @@ +# done typo + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + while false do + skip + dono +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/while/dooErr.wacc b/src/test/wacc/waccPrograms/syntaxErr/while/dooErr.wacc new file mode 100644 index 0000000..0cbc70f --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/while/dooErr.wacc @@ -0,0 +1,15 @@ +# do typo + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + while false doo + skip + done +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/while/whilErr.wacc b/src/test/wacc/waccPrograms/syntaxErr/while/whilErr.wacc new file mode 100644 index 0000000..740bad9 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/while/whilErr.wacc @@ -0,0 +1,15 @@ +# while typo + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + whil false do + skip + done +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/while/whileNodo.wacc b/src/test/wacc/waccPrograms/syntaxErr/while/whileNodo.wacc new file mode 100644 index 0000000..dac1095 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/while/whileNodo.wacc @@ -0,0 +1,15 @@ +# while missing opening do + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + while false + skip + done +end diff --git a/src/test/wacc/waccPrograms/syntaxErr/while/whileNodone.wacc b/src/test/wacc/waccPrograms/syntaxErr/while/whileNodone.wacc new file mode 100644 index 0000000..f88c7d5 --- /dev/null +++ b/src/test/wacc/waccPrograms/syntaxErr/while/whileNodone.wacc @@ -0,0 +1,14 @@ +# while missing closing done + +# Output: +# #syntax_error# + +# Exit: +# 100 + +# Program: + +begin + while false do + skip +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/hashInProgram.wacc b/src/test/wacc/waccPrograms/valid/IO/print/hashInProgram.wacc new file mode 100644 index 0000000..d59d890 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/hashInProgram.wacc @@ -0,0 +1,15 @@ +# In-line comments and printing # + +# Output: +# We can print the hash character: # +# We can also print # when its in a string. +# + +# Program: + +begin + int x = 0 ; # comments can be in-line + print "We can print the hash character: " ; + println '#' ; + println "We can also print # when its in a string." +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/multipleStringsAssignment.wacc b/src/test/wacc/waccPrograms/valid/IO/print/multipleStringsAssignment.wacc new file mode 100644 index 0000000..49a1581 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/multipleStringsAssignment.wacc @@ -0,0 +1,40 @@ +# multiple string assignments and comparisons + +# Output: +# s1 is Hi +# s2 is Hello +# They are not the same string. +# Now make s1 = s2 +# s1 is Hello +# s2 is Hello +# They are the same string. +# + +# Program: + +begin + string s1 = "Hi" ; + string s2 = "Hello" ; + print "s1 is " ; + println s1 ; + print "s2 is " ; + println s2 ; + if s1 == s2 then + println "They are the same string." + else + println "They are not the same string." + fi ; + + println "Now make s1 = s2" ; + s1 = s2 ; + + print "s1 is " ; + println s1 ; + print "s2 is " ; + println s2 ; + if s1 == s2 then + println "They are the same string." + else + println "They are not the same string." + fi +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/print-backspace.wacc b/src/test/wacc/waccPrograms/valid/IO/print/print-backspace.wacc new file mode 100644 index 0000000..28e7f28 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/print-backspace.wacc @@ -0,0 +1,11 @@ +# simple print statement off a string with a backspace character + +# Output: +# Hello World! +# + +# Program: + +begin + print "Hello\b World!\n" +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/print.wacc b/src/test/wacc/waccPrograms/valid/IO/print/print.wacc new file mode 100644 index 0000000..fab4fe2 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/print.wacc @@ -0,0 +1,10 @@ +# simple print statement + +# Output: +# Hello World! + +# Program: + +begin + print "Hello World!" +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/printBool.wacc b/src/test/wacc/waccPrograms/valid/IO/print/printBool.wacc new file mode 100644 index 0000000..ec051f1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/printBool.wacc @@ -0,0 +1,16 @@ +# basic Boolean printing + +# Output: +# True is true +# False is false +# + +# Program: + +begin + print "True is " ; + println true ; + + print "False is " ; + println false +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/printChar.wacc b/src/test/wacc/waccPrograms/valid/IO/print/printChar.wacc new file mode 100644 index 0000000..fda2902 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/printChar.wacc @@ -0,0 +1,12 @@ +# basic character printing + +# Output: +# A simple character example is f +# + +# Program: + +begin + print "A simple character example is " ; + println 'f' +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/printCharArray.wacc b/src/test/wacc/waccPrograms/valid/IO/print/printCharArray.wacc new file mode 100644 index 0000000..98090ca --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/printCharArray.wacc @@ -0,0 +1,15 @@ +# printing the contents of a char[] is possible via an intermediate variable + +# Output: +# hi! +# + +# Exit: +# 0 + +# Program: + +begin + char[] s = ['h','i','!']; + println s +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/printCharAsString.wacc b/src/test/wacc/waccPrograms/valid/IO/print/printCharAsString.wacc new file mode 100644 index 0000000..3e4286c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/printCharAsString.wacc @@ -0,0 +1,15 @@ +# character array treated as a string + +# Output: +# foo +# bar +# + +# Program: + +begin + char[] str = ['f','o','o']; + println str; + str = ['b','a','r']; + println str +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/printEscChar.wacc b/src/test/wacc/waccPrograms/valid/IO/print/printEscChar.wacc new file mode 100644 index 0000000..f44e765 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/printEscChar.wacc @@ -0,0 +1,12 @@ +# basic escaped character printing + +# Output: +# An escaped character example is " +# + +# Program: + +begin + print "An escaped character example is " ; + println '\"' +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/printInt.wacc b/src/test/wacc/waccPrograms/valid/IO/print/printInt.wacc new file mode 100644 index 0000000..e4179b0 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/printInt.wacc @@ -0,0 +1,12 @@ +# basic Integer printing + +# Output: +# An example integer is 189 +# + +# Program: + +begin + print "An example integer is " ; + println 189 +end diff --git a/src/test/wacc/waccPrograms/valid/IO/print/println.wacc b/src/test/wacc/waccPrograms/valid/IO/print/println.wacc new file mode 100644 index 0000000..70fe50a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/print/println.wacc @@ -0,0 +1,11 @@ +# simple println statement + +# Output: +# Hello World! +# + +# Program: + +begin + println "Hello World!" +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/echoBigInt.wacc b/src/test/wacc/waccPrograms/valid/IO/read/echoBigInt.wacc new file mode 100644 index 0000000..0c2731a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/echoBigInt.wacc @@ -0,0 +1,17 @@ +# echo the user's input int + +# Input: 2147483647 + +# Output: +# enter an integer to echo +# 2147483647 +# + +# Program: + +begin + int x = 1 ; + println "enter an integer to echo"; + read x ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/echoBigNegInt.wacc b/src/test/wacc/waccPrograms/valid/IO/read/echoBigNegInt.wacc new file mode 100644 index 0000000..4f3fb0d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/echoBigNegInt.wacc @@ -0,0 +1,17 @@ +# echo the user's input int + +# Input: -2147483648 + +# Output: +# enter an integer to echo +# -2147483648 +# + +# Program: + +begin + int x = 1 ; + println "enter an integer to echo"; + read x ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/echoChar.wacc b/src/test/wacc/waccPrograms/valid/IO/read/echoChar.wacc new file mode 100644 index 0000000..f306cc1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/echoChar.wacc @@ -0,0 +1,17 @@ +# echo the user's input char + +# Input: K + +# Output: +# enter a character to echo +# K +# + +# Program: + +begin + char c = '\0' ; + println "enter a character to echo"; + read c ; + println c +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/echoInt.wacc b/src/test/wacc/waccPrograms/valid/IO/read/echoInt.wacc new file mode 100644 index 0000000..871a239 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/echoInt.wacc @@ -0,0 +1,17 @@ +# echo the user's input int + +# Input: 101 + +# Output: +# enter an integer to echo +# 101 +# + +# Program: + +begin + int x = 1 ; + println "enter an integer to echo"; + read x ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/echoNegInt.wacc b/src/test/wacc/waccPrograms/valid/IO/read/echoNegInt.wacc new file mode 100644 index 0000000..740b718 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/echoNegInt.wacc @@ -0,0 +1,17 @@ +# echo the user's input int + +# Input: -5 + +# Output: +# enter an integer to echo +# -5 +# + +# Program: + +begin + int x = 1 ; + println "enter an integer to echo"; + read x ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/echoPuncChar.wacc b/src/test/wacc/waccPrograms/valid/IO/read/echoPuncChar.wacc new file mode 100644 index 0000000..c132f4b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/echoPuncChar.wacc @@ -0,0 +1,17 @@ +# echo the user's input char + +# Input: ! + +# Output: +# enter a character to echo +# ! +# + +# Program: + +begin + char c = '\0' ; + println "enter a character to echo"; + read c ; + println c +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/read.wacc b/src/test/wacc/waccPrograms/valid/IO/read/read.wacc new file mode 100644 index 0000000..8acf033 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/read.wacc @@ -0,0 +1,15 @@ +# simple read statement + +#Input: 350 + +# Output: +# input an integer to continue... +# + +# Program: + +begin + int x = 10; + println "input an integer to continue..." ; + read x +end diff --git a/src/test/wacc/waccPrograms/valid/IO/read/readAtEof.wacc b/src/test/wacc/waccPrograms/valid/IO/read/readAtEof.wacc new file mode 100644 index 0000000..528ffe4 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/read/readAtEof.wacc @@ -0,0 +1,19 @@ +# Read should not alter nothing if it can't read anything +# Thanks for Nixon Moony-Enraght for catching this! + +# Input: X + +# Output: +# XZ +# + +# Program: +begin + char c = 'Z'; + read c; + print c; + + char c2 = 'Z'; + read c2; + println c2 +end diff --git a/src/test/wacc/waccPrograms/valid/IO/special/IOLoop.wacc b/src/test/wacc/waccPrograms/valid/IO/special/IOLoop.wacc new file mode 100644 index 0000000..4efdb9d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/special/IOLoop.wacc @@ -0,0 +1,40 @@ +# simple input/output loop + +# Input: 1 Y 2 Y 3 Y 4 Y 5 Y 142 N + +# Output: +# Please input an integer: echo input: 1 +# Do you want to continue entering input? +# (enter Y for 'yes' and N for 'no') +# Please input an integer: echo input: 2 +# Do you want to continue entering input? +# (enter Y for 'yes' and N for 'no') +# Please input an integer: echo input: 3 +# Do you want to continue entering input? +# (enter Y for 'yes' and N for 'no') +# Please input an integer: echo input: 4 +# Do you want to continue entering input? +# (enter Y for 'yes' and N for 'no') +# Please input an integer: echo input: 5 +# Do you want to continue entering input? +# (enter Y for 'yes' and N for 'no') +# Please input an integer: echo input: 142 +# Do you want to continue entering input? +# (enter Y for 'yes' and N for 'no') +# + +# Program: + +begin + char continue = 'Y' ; + int buff = 0 ; + while continue != 'N' do + print "Please input an integer: " ; + read buff ; + print "echo input: " ; + println buff ; + println "Do you want to continue entering input?" ; + println "(enter Y for \'yes\' and N for \'no\')" ; + read continue + done +end diff --git a/src/test/wacc/waccPrograms/valid/IO/special/IOSequence.wacc b/src/test/wacc/waccPrograms/valid/IO/special/IOSequence.wacc new file mode 100644 index 0000000..415da3b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/IO/special/IOSequence.wacc @@ -0,0 +1,17 @@ +# basic input/output sequence + +# Input: 37 + +# Output: +# Please input an integer: You input: 37 +# + +# Program: + +begin + int x = 0 ; + print "Please input an integer: " ; + read x ; + print "You input: " ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/advanced/binarySortTree.wacc b/src/test/wacc/waccPrograms/valid/advanced/binarySortTree.wacc new file mode 100644 index 0000000..913f427 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/advanced/binarySortTree.wacc @@ -0,0 +1,83 @@ +# The program reads n (number of integers), then n integers. After each input, +# it insert the integer into a binary search tree. At the end, it prints out +# the content in the binary search tree so that we have a sorted list of +# integer. +# +# We represent a node in the binary search tree using two pair elements. The +# first element has a type , the int is the integer stored in the +# node, the pair is the pointer to the second pair element. The second pair +# element has a type which is the pointer to the two children +# nodes in the binary search tree. + +begin + + # Create a new node of a binary search tree having the given integer value + # and points to the two given pairs. + pair(int, pair) createNewNode(int value, pair(int, pair) left, pair(int, pair) right) is + pair(pair, pair) p = newpair(left, right) ; + pair(int, pair) q = newpair(value, p) ; + return q + end + + # Given a root of a binary search tree and an integer to insert, the function + # inserts the integer into the tree and returns the new root of the tree. + pair(int, pair) insert(pair(int, pair) root, int n) is + if root == null then + root = call createNewNode(n, null, null) + else + pair(pair, pair) p = snd root ; + int current = fst root ; + pair(int, pair) q = null ; + if n < current then + q = fst p ; + fst p = call insert(q, n) + else + q = snd p ; + snd p = call insert(q, n) + fi + fi ; + return root + end + + # Print the integers in the binary search tree in the increasing order. + int printTree(pair(int, pair) root) is + if root == null then + return 0 + else + pair(pair, pair) body = snd root ; + pair(int, pair) p = fst body ; + int temp = call printTree(p) ; + temp = fst root ; + print temp ; + print ' ' ; + p = snd body ; + temp = call printTree(p) ; + return 0 + fi + end + + # The main function + int n = 0 ; + print "Please enter the number of integers to insert: " ; + read n ; + print "There are " ; + print n ; + println " integers." ; + int i = 0 ; + pair(int, pair) root = null ; + while i < n do + int x = 0 ; + print "Please enter the number at position " ; + print i + 1 ; + print " : " ; + read x ; + root = call insert(root, x) ; + i = i + 1 + done ; + print "Here are the numbers sorted: " ; + i = call printTree(root) ; + println "" +end + + + diff --git a/src/test/wacc/waccPrograms/valid/advanced/hashTable.wacc b/src/test/wacc/waccPrograms/valid/advanced/hashTable.wacc new file mode 100644 index 0000000..d7d196e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/advanced/hashTable.wacc @@ -0,0 +1,321 @@ +# This program is interactive. We implement a hash table containing integers and we play with it. +# +# A hash table is represented by an array of node lists. Each node in a node list is represented +# by pair(int, pair). The first element of the pair is the integer at that node, the second element +# is the pointer to the next node (or null if no more node). +# +# Integers those mapped to the same index are stored on the list (in any order) in that index. + +begin + + ######################### Functions for Hash Table Interface ############################### + + # Given a hash table, initialise it. Return true. + bool init(pair(int, pair)[] table) is + int length = len table ; + int i = 0 ; + while i < length do + table[i] = null ; + i = i + 1 + done ; + return true + end + + # Returns true if and only if the given hash table contains x. + bool contain(pair(int, pair)[] table, int x) is + int index = call calculateIndex(table, x) ; + pair(int, pair) node = call findNode(table[index], x) ; + return node != null + end + + # Insert the given x into the hash table if it does not already contain x. + # Returns true if and only if the table does not already contain x. + bool insertIfNotContain(pair(int, pair)[] table, int x) is + int index = call calculateIndex(table, x) ; + pair(int, pair) node = call findNode(table[index], x) ; + if node != null then + # Already contain it. Do nothing. + return false + else + # Insert in the front of the list. + pair(int, pair) p = newpair(x, table[index]) ; + table[index] = p ; + return true + fi + end + + # Remove the given x from the hash table. Returns true if an only if the table contains x. + # Otherwise, do nothing and returns false. + bool remove(pair(int, pair)[] table, int x) is + int index = call calculateIndex(table, x) ; + pair(int, pair) node = call findNode(table[index], x) ; + if node == null then + # Not found x. Just return false. + return false + else + # Found x, have to remove the node. + table[index] = call removeNode(table[index], node) ; + return true + fi + end + + # Remove all nodes from the table. Returns true. + bool removeAll(pair(int, pair)[] table) is + int length = len table ; + int i = 0 ; + while i < length do + pair(int, pair) p = table[i] ; + while p != null do + pair(int, pair) p2 = snd p ; + free p ; + p = p2 + done ; + table[i] = null ; + i = i + 1 + done ; + return true + end + + # Count the number of integers in the table and return it. + int count(pair(int, pair)[] table) is + int length = len table ; + int sum = 0 ; + int i = 0 ; + while i < length do + int subSum = call countNodes(table[i]) ; + sum = sum + subSum ; + i = i + 1 + done ; + return sum + end + + # Print all the integers inside the table, separated by a space and ended with a newline. Returns true. + bool printAll(pair(int, pair)[] table) is + int length = len table ; + int i = 0 ; + while i < length do + bool result = call printAllNodes(table[i]) ; + i = i + 1 + done ; + println "" ; + return true + end + + # A helper function. + # Given a hash table and an integer, calculate the index of the integer in the table. + int calculateIndex(pair(int, pair)[] table, int x) is + int length = len table ; + return x % length + end + + # A helper function. + # Given a head of a chain of nodes, returns the first node containing the value x. + # Returns null if no such node. + pair(int, pair) findNode(pair(int, pair) head, int x) is + while head != null do + int y = fst head ; + if y == x then + return head + else + head = snd head + fi + done ; + return null + end + + # A helper function. + # Given a list of nodes and a node to remove, remove that node from the + # list and return the new list. + pair(int, pair) removeNode(pair(int, pair) head, pair(int, pair) toRemove) is + if head == null then + # Should not happen actually. + return null + else + if head == toRemove then + # Save the new head. + head = snd head ; + + # Deallocate the memory of the old node. + free toRemove ; + + # Return the new head. + return head + else + # Not this node, recursive. + pair(int, pair) tail = snd head ; + snd head = call removeNode(tail, toRemove) ; + return head + fi + fi + end + + # A helper function. + # Given a list of nodes, count how many nodes there are. + int countNodes(pair(int, pair) head) is + int sum = 0 ; + while head != null do + sum = sum + 1 ; + head = snd head + done ; + return sum + end + + # A helper function. + # Given a list of nodes, print each integer in the node followed by a space. Returns true. + bool printAllNodes(pair(int, pair) head) is + while head != null do + int x = fst head ; + print x ; + print ' ' ; + head = snd head + done ; + return true + end + + ######################### Functions for Command Line Interface ############################### + + # Print the menu and ask to choose. Returns a valid decision. + char printMenu() is + println "===========================================" ; + println "========== Hash Table Program =============" ; + println "===========================================" ; + println "= =" ; + println "= Please choose the following options: =" ; + println "= =" ; + println "= a: insert an integer =" ; + println "= b: find an integer =" ; + println "= c: count the integers =" ; + println "= d: print all integers =" ; + println "= e: remove an integer =" ; + println "= f: remove all integers =" ; + println "= g: exit =" ; + println "= =" ; + println "===========================================" ; + + int minChoice = ord 'a' ; + int maxChoice = ord 'g' ; + + while true do + print "Your decision: " ; + char d = '\0' ; + read d ; + int dInt = ord d ; + if minChoice <= dInt && dInt <= maxChoice then + return d + else + print "You have entered: " ; + print d ; + println " which is invalid, please try again." + fi + done ; + # The compiler is not smart enough to know that this never reaches. + # We have to add a return statement here. + return '\0' + end + + # Print out the question, and then read an integer. After that print the integer back and return it. + int askForInt(string message) is + print message ; + int x = 0 ; + read x ; + print "You have entered: " ; + println x ; + return x + end + + # Handle menu insert. Returns true. + bool handleMenuInsert(pair(int, pair)[] table) is + int x = call askForInt("Please enter an integer to insert: ") ; + bool notContain = call insertIfNotContain(table, x) ; + if notContain then + println "Successfully insert it. The integer is new." + else + println "The integer is already there. No insertion is made." + fi ; + return true + end + + # Handle menu find. Returns true. + bool handleMenuFind(pair(int, pair)[] table) is + int x = call askForInt("Please enter an integer to find: ") ; + bool find = call contain(table, x) ; + if find then + println "Find the integer." + else + println "The integer is not found." + fi ; + return true + end + + # Handle menu count. Returns true. + bool handleMenuCount(pair(int, pair)[] table) is + int size = call count(table) ; + if size == 1 then + println "There is only 1 integer." + else + print "There are " ; + print size ; + println " integers." + fi ; + return true + end + + # Handle menu print. Returns true. + bool handleMenuPrint(pair(int, pair)[] table) is + print "Here are the integers: " ; + bool junk = call printAll(table) ; + return true + end + + # Handle menu remove. Returns true. + bool handleMenuRemove(pair(int, pair)[] table) is + int x = call askForInt("Please enter an integer to remove: ") ; + bool find = call remove(table, x) ; + if find then + println "The integer has been removed." + else + println "The integer is not found." + fi ; + return true + end + + # Handle menu remove all. Returns true. + bool handleMenuRemoveAll(pair(int, pair)[] table) is + bool junk = call removeAll(table) ; + println "All integers have been removed." ; + return true + end + + ################################# The main function ######################################## + # Our hash table of size 13. + pair(int, pair)[] table = [null, null, null, null, null, null, null, null, null, null, null, null, null] ; + bool junk = call init(table) ; + + bool continue = true ; + while continue do + char choice = call printMenu() ; + if choice == 'a' then + bool result = call handleMenuInsert(table) + else if choice == 'b' then + bool result = call handleMenuFind(table) + else if choice == 'c' then + bool result = call handleMenuCount(table) + else if choice == 'd' then + bool result = call handleMenuPrint(table) + else if choice == 'e' then + bool result = call handleMenuRemove(table) + else if choice == 'f' then + bool result = call handleMenuRemoveAll(table) + else if choice == 'g' then + println "Goodbye Human" ; + continue = false + else + # Should not happen. + print "Error: unknown choice (" ; + print choice ; + println ")" ; + exit -1 + fi fi fi fi fi fi fi + done + +end diff --git a/src/test/wacc/waccPrograms/valid/advanced/ticTacToe.wacc b/src/test/wacc/waccPrograms/valid/advanced/ticTacToe.wacc new file mode 100644 index 0000000..c9cefec --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/advanced/ticTacToe.wacc @@ -0,0 +1,1097 @@ +# This is a program that allows a human to play Tic Tac Toe with a smart AI. +# The AI is implemented using minimax approach. The AI is a perfect one, meaning +# that it will never lose and if there is an immediate win, it will play that. +# +# It takes quite a long time to initialise and free up the memory for the AI. +# All observed bugs have been fixed. +# +# In this program, we are very often need the memory from the heap. +# But because a pair is the only data type allocated on the heap, +# we have to use it. We very often need a data structure to store +# a set of 3 objects. We will use the format below. +# +# root --\ +# | +# \|/ +# +# +----------+----------+ +# | | | +# | front | object 3 | +# | | | +# +----------+----------+ +# | +# | +# \|/ +# +# +----------+----------+ +# | | | +# | object 1 | object 2 | +# | | | +# +----------+----------+ +# +# We call this structure Pair4Three. + +# Program: + +begin + + ############################## Interface Functions ###################### + + # Print greeting message and ask the player to choose their symbol. + # Return either 'x' or 'o'. 'x' plays first. + char chooseSymbol() is + println "========= Tic Tac Toe ================" ; + println "= Because we know you want to win =" ; + println "======================================" ; + println "= =" ; + println "= Who would you like to be? =" ; + println "= x (play first) =" ; + println "= o (play second) =" ; + println "= q (quit) =" ; + println "= =" ; + println "======================================" ; + + char chosen = '\0' ; + while chosen == '\0' do + print "Which symbol you would like to choose: " ; + char c = '\0' ; + read c ; + if c == 'x' || c == 'X' then + chosen = 'x' + else + if c == 'o' || c == 'O' then + chosen = 'o' + else + if c == 'q' || c == 'Q' then + println "Goodbye safety." ; + exit 0 + else + print "Invalid symbol: " ; + println c ; + println "Please try again." + fi + fi + fi + done ; + print "You have chosen: " ; + println chosen ; + return chosen + end + + # Print the board out to the screen. + bool printBoard(pair(pair, pair) board) is + pair(pair, pair) front = fst board ; + pair(pair, char) row1 = fst front ; + pair(pair, char) row2 = snd front ; + pair(pair, char) row3 = snd board ; + + println " 1 2 3"; + print "1"; + bool _ = call printRow(row1) ; + println " -+-+-" ; + print "2"; + _ = call printRow(row2) ; + println " -+-+-" ; + print "3"; + _ = call printRow(row3) ; + println ""; + return true + end + + # Print a row with a newline to the screen. + bool printRow(pair(pair, char) row) is + pair(char, char) front = fst row ; + + char cell1 = fst front ; + char cell2 = snd front ; + char cell3 = snd row ; + + bool _ = call printCell(cell1) ; + print '|' ; + _ = call printCell(cell2) ; + print '|' ; + _ = call printCell(cell3) ; + println "" ; + return true + end + + # Print a given cell. Print an empty space if it is empty. Return true. + bool printCell(char cell) is + if cell == '\0' then + print ' ' + else + print cell + fi ; + return true + end + + # Ask for a move from the human player. The valid move is then stored in the given move array. + # The row number is stored at move[0], the column number is stored at move[1]. Return true. + bool askForAMoveHuman(pair(pair, pair) board, int[] move) is + bool success = false ; + int row = 0 ; + int column = 0 ; + + while !success do + println "What is your next move?" ; + print " row (1-3): " ; + read row ; + print " column (1-3): " ; + read column ; + success = call validateMove(board, row, column) ; + + if success then + println "" ; # Just print out an empty line + move[0] = row ; + move[1] = column ; + return true + else + println "Your move is invalid. Please try again." + fi + done ; + # Should not reach here + return true + end + + # Validate that the give move is valid. Returns true iff it is valid. + bool validateMove(pair(pair, pair) board, int moveRow, int moveColumn) is + if 1 <= moveRow && moveRow <= 3 && 1 <= moveColumn && moveColumn <= 3 then + char sym = call symbolAt(board, moveRow, moveColumn) ; + # Make sure that the cell is empty + return sym == '\0' + else + return false + fi + end + + # Print out to the screen about a recent move maid by the AI. Return true. + bool notifyMoveHuman(pair(pair, pair) board, char currentTurn, char playerSymbol, int moveRow, int moveColumn) is + print "The AI played at row " ; + print moveRow ; + print " column " ; + println moveColumn ; + return true + end + + ############################### AI Functions ######################################### + + # Initialise an AI data. + pair(pair, pair) initAI(char aiSymbol) is + + pair(char, pair) info = newpair(aiSymbol, null) ; # Don't know yet how to use the second element. + pair(pair, int) stateTree = call generateAllPossibleStates(aiSymbol) ; + int value = call setValuesForAllStates(stateTree, aiSymbol, 'x') ; + + pair(pair, pair) aiData = newpair(info, stateTree) ; + return aiData + end + + # Generate the whole tree of all states. Then return the tree. + pair(pair, int) generateAllPossibleStates(char aiSymbol) is + pair(pair, pair) board = call allocateNewBoard() ; + pair(pair, int) rootState = call convertFromBoardToState(board) ; + rootState = call generateNextStates(rootState, 'x') ; + return rootState + end + + # Convert from a board to a state. + # A state consists of 3 objects: the board, the pointers to the next states, and the value for this state (int). + # Therefore, we use the Pair4Three structure. + pair(pair, int) convertFromBoardToState(pair(pair, pair) board) is + + pair(pair, pair) pointers = call generateEmptyPointerBoard() ; + pair(pair, pair) front = newpair(board, pointers) ; + pair(pair, int) state = newpair(front, 0) ; # The initial value of 0 will be replaced. + + return state + end + + # Allocate memory for the pointers to the next state. + # It looks like a board, but contains pointers (pairs) instead of chars. + pair(pair, pair) generateEmptyPointerBoard() is + + pair(pair, pair) row1 = call generateEmptyPointerRow() ; + pair(pair, pair) row2 = call generateEmptyPointerRow() ; + pair(pair, pair) row3 = call generateEmptyPointerRow() ; + + pair(pair, pair) front = newpair(row1, row2) ; + pair(pair, pair) root = newpair(front, row3) ; + return root + + end + + # Allocate memory for the 3 pointers to the next state of a row. + pair(pair, pair) generateEmptyPointerRow() is + pair(pair, pair) front = newpair(null, null) ; + pair(pair, pair) root = newpair(front, null) ; + return root + end + + # Generate next states recursively. Returns the state. + pair(pair, int) generateNextStates(pair(pair, int) state, char currentPlayer) is + pair(pair, pair) front = fst state ; + + pair(pair, pair) board = fst front ; + pair(pair, pair) pointers = snd front ; + + char previousPlayer = call oppositeSymbol(currentPlayer) ; + + bool won = call hasWon(board, previousPlayer) ; + + if won then + # The game ends. The winner is known. + return state + else + bool _ = call generateNextStatesBoard(board, pointers, currentPlayer) ; + return state + fi + + end + + # Generate Just the next states for every possible point on the board. Update the pointers accordingly. Return true. + bool generateNextStatesBoard(pair(pair, pair) board, pair(pair, pair) pointers, char currentPlayer) is + pair(pair, pair) front = fst board ; + + pair(pair, char) row1 = fst front ; + pair(pair, char) row2 = snd front ; + pair(pair, char) row3 = snd board ; + + pair(pair, pair) frontP = fst pointers ; + + pair(pair, pair) row1P = fst frontP ; + pair(pair, pair) row2P = snd frontP ; + pair(pair, pair) row3P = snd pointers ; + + bool _ = call generateNextStatesRow(board, row1, row1P, currentPlayer, 1) ; + _ = call generateNextStatesRow(board, row2, row2P, currentPlayer, 2) ; + _ = call generateNextStatesRow(board, row3, row3P, currentPlayer, 3) ; + + return true + end + + # Generate Just the next states for every possible point on the row. Update the pointers accordingly. Return true. + bool generateNextStatesRow(pair(pair, pair) board, pair(pair, char) row, pair(pair, pair) pointerRow, char currentPlayer, int rowNumber) is + pair(char, char) front = fst row ; + + char cell1 = fst front ; + char cell2 = snd front ; + char cell3 = snd row ; + + pair(pair, pair) frontP = fst pointerRow ; + + fst frontP = call generateNextStatesCell(board, cell1, currentPlayer, rowNumber, 1) ; + snd frontP = call generateNextStatesCell(board, cell2, currentPlayer, rowNumber, 2) ; + snd pointerRow = call generateNextStatesCell(board, cell3, currentPlayer, rowNumber, 3) ; + + return true + end + + # Generate Just the next states for the cell on the board. Returns the pointer to the next state. + pair(pair, int) generateNextStatesCell(pair(pair, pair) board, char cell, char currentPlayer, int rowNumber, int columnNumber) is + if cell == '\0' then + # If the cell is empty, generate the next state. + pair(pair, pair) board2 = call cloneBoard(board) ; + bool _ = call placeMove(board2, currentPlayer, rowNumber, columnNumber) ; + pair(pair, int) state = call convertFromBoardToState(board2) ; + char nextPlayer = call oppositeSymbol(currentPlayer) ; + + # Generate next states recursively and return it out. + state = call generateNextStates(state, nextPlayer) ; + return state + else + # If the cell is not empty, return null. + return null + fi + end + + # Clone board. + pair(pair, pair) cloneBoard(pair(pair, pair) board) is + pair(pair, pair) board2 = call allocateNewBoard() ; + bool _ = call copyBoard(board, board2) ; + return board2 + end + + # Copy the content of one board to another. Return true. + bool copyBoard(pair(pair, pair) from, pair(pair, pair) to) is + pair(pair, pair) frontFrom = fst from ; + pair(pair, char) row1From = fst frontFrom ; + pair(pair, char) row2From = snd frontFrom ; + pair(pair, char) row3From = snd from ; + + pair(pair, pair) frontTo = fst to ; + pair(pair, char) row1To = fst frontTo ; + pair(pair, char) row2To = snd frontTo ; + pair(pair, char) row3To = snd to ; + + bool _ = call copyRow(row1From, row1To) ; + _ = call copyRow(row2From, row2To) ; + _ = call copyRow(row3From, row3To) ; + + return true + end + + # Copy from one board row to another. Return true. + bool copyRow(pair(pair, char) from, pair(pair, char) to) is + pair(char, char) frontFrom = fst from ; + pair(char, char) frontTo = fst to ; + + fst frontTo = fst frontFrom ; + snd frontTo = snd frontFrom ; + snd to = snd from ; + return true + end + + # Calculate the value of how good each state is using Minimax approach. + # If AI wins, value = 100. + # If AI lose, value = -100. + # If Stalemate, value = 0. + # Otherwise, combine the values from the next states. + # If this state is null, then return -101 if it is a max state, 101 if it is a min state (thus those values will not be picked). + # Return the value. + int setValuesForAllStates(pair(pair, int) state, char aiSymbol, char currentPlayer) is + int outValue = 0 ; + if state == null then + # The current state is impossible to reach. + # Assign a value that will not be picked in the future. + if currentPlayer == aiSymbol then + # Later on, we will pick the lowest value (min). So we set this value high so that it will not be picked. + outValue = 101 + else + # Later on, we will pick the highest value (max). So we set this value low so that it will not be picked. + outValue = -101 + fi + else + + pair(pair, pair) front = fst state ; + + pair(pair, pair) board = fst front ; + pair(pair, pair) pointers = snd front ; + + char anotherPlayer = call oppositeSymbol(currentPlayer) ; + + # The current player is about to play. So if another player has won it already, the current player cannot play it. + bool won = call hasWon(board, anotherPlayer) ; + + if won then + if anotherPlayer == aiSymbol then + outValue = 100 # We won + else + outValue = -100 # We lost + fi + else + bool hasEmptyCell = call containEmptyCell(board) ; + if hasEmptyCell then + # If can do next move, calculate the value from the next states. + outValue = call calculateValuesFromNextStates(pointers, aiSymbol, anotherPlayer) ; + + # In order for the AI to choose the winning move immediately, we have to reduce the value for those not winning yet. + # So if the next move has value 100, we set the value of this move 90. + if outValue == 100 then + outValue = 90 + else + skip + fi + else + # Otherwise, it is a stalemate. + outValue = 0 + fi + fi ; + snd state = outValue + fi ; + return outValue + end + + # Calculate the values for each next state, then combine them to get the value of this state. Return the value. + int calculateValuesFromNextStates(pair(pair, pair) pointers, char aiSymbol, char playerOfNextState) is + pair(pair, pair) front = fst pointers ; + + pair(pair, pair) row1 = fst front ; + pair(pair, pair) row2 = snd front ; + pair(pair, pair) row3 = snd pointers ; + + int value1 = call calculateValuesFromNextStatesRow(row1, aiSymbol, playerOfNextState) ; + int value2 = call calculateValuesFromNextStatesRow(row2, aiSymbol, playerOfNextState) ; + int value3 = call calculateValuesFromNextStatesRow(row3, aiSymbol, playerOfNextState) ; + + int out = call combineValue(aiSymbol, playerOfNextState, value1, value2, value3) ; + return out + end + + # Calculate the values for each next state in a row, then combine them to get the value of this row. Return the value. + int calculateValuesFromNextStatesRow(pair(pair, pair) rowPointers, char aiSymbol, char playerOfNextState) is + pair(pair, pair) front = fst rowPointers ; + + pair(pair, int) state1 = fst front ; + pair(pair, int) state2 = snd front ; + pair(pair, int) state3 = snd rowPointers ; + + int value1 = call setValuesForAllStates(state1, aiSymbol, playerOfNextState) ; + int value2 = call setValuesForAllStates(state2, aiSymbol, playerOfNextState) ; + int value3 = call setValuesForAllStates(state3, aiSymbol, playerOfNextState) ; + + int out = call combineValue(aiSymbol, playerOfNextState, value1, value2, value3) ; + return out + end + + int combineValue(char aiSymbol, char playerOfNextState, int value1, int value2, int value3) is + int out = 0 ; + if aiSymbol == playerOfNextState then + # We move next so the human moves now. Pick the lowest value. + out = call min3(value1, value2, value3) + else + # Human moves next so we move now. Pick the highest value. + out = call max3(value1, value2, value3) + fi ; + return out + end + + # Find the minimum of the three. + int min3(int a, int b, int c) is + if a < b then + if a < c then + return a + else + return c + fi + else + if b < c then + return b + else + return c + fi + fi + end + + # Find the maximum of the three. + int max3(int a, int b, int c) is + if a > b then + if a > c then + return a + else + return c + fi + else + if b > c then + return b + else + return c + fi + fi + end + + # Destroy all memory used by the AI. Return true. + bool destroyAI(pair(pair, pair) aiData) is + + pair(char, pair) info = fst aiData ; + pair(pair, int) stateTree = snd aiData ; + + bool _ = call deleteStateTreeRecursively(stateTree) ; + free info ; + free aiData ; + return true + end + + # Ask the AI for a new move. Return true. + bool askForAMoveAI(pair(pair, pair) board, char currentTurn, char playerSymbol, pair(pair, pair) aiData, int[] move) is + + pair(char, pair) info = fst aiData ; + pair(pair, int) stateTree = snd aiData ; + + pair(pair, pair) front = fst stateTree ; + + pair(pair, pair) pointers = snd front ; + int stateValue = snd stateTree ; + + bool _ = call findTheBestMove(pointers, stateValue, move) ; + + println "AI is cleaning up its memory..." ; + # Update the state tree by using the new move. + snd aiData = call deleteAllOtherChildren(pointers, move[0], move[1]) ; + + _ = call deleteThisStateOnly(stateTree) ; + return true + end + + # Given the pointers to all next states, pick the first one with the given stateValue and store the move in the the given array. + # Return true. + bool findTheBestMove(pair(pair, pair) pointers, int stateValue, int[] move) is + + # We have a hack by changing the state value to 90 if the next state is 100. + # So if we have a state value of 90, look for the one with 100 first. + # If found, use it. Otherwise, look for the one with 90. + if stateValue == 90 then + bool found = call findMoveWithGivenValue(pointers, 100, move) ; + if found then + return true + else + skip + fi + else + skip + fi ; + + # Normal case. Or when cannot find the child with 100. + bool found = call findMoveWithGivenValue(pointers, stateValue, move) ; + if found then + return true + else + # Should not happen. Cannot find such move. + println "Internal Error: cannot find the next move for the AI" ; + exit -1 + fi + + end + + # Given the pointers to all next states, pick the first one with the given stateValue and store the move in the the given array. + # Return true in this case. Otherwise, the move array is untouched and return false. + bool findMoveWithGivenValue(pair(pair, pair) pointers, int stateValue, int[] move) is + pair(pair, pair) front = fst pointers ; + + pair(pair, pair) row1 = fst front ; + pair(pair, pair) row2 = snd front ; + pair(pair, pair) row3 = snd pointers ; + + bool find = call findMoveWithGivenValueRow(row1, stateValue, move) ; + if find then + move[0] = 1 + else + find = call findMoveWithGivenValueRow(row2, stateValue, move) ; + if find then + move[0] = 2 + else + find = call findMoveWithGivenValueRow(row3, stateValue, move) ; + if find then + move[0] = 3 + else + # Not found, return false. + return false + fi + fi + fi ; + return true + end + + # Given a row of pointers, pick the first one with the given stateValue and store in move[1], return true if such child state is found. Otherwise, return false and move[1] is untouched. + bool findMoveWithGivenValueRow(pair(pair, pair) rowPointers, int stateValue, int[] move) is + + pair(pair, pair) front = fst rowPointers ; + + pair(pair, int) cell1 = fst front ; + pair(pair, int) cell2 = snd front ; + pair(pair, int) cell3 = snd rowPointers ; + + bool find = call hasGivenStateValue(cell1, stateValue) ; + if find then + move[1] = 1 + else + find = call hasGivenStateValue(cell2, stateValue) ; + if find then + move[1] = 2 + else + find = call hasGivenStateValue(cell3, stateValue) ; + if find then + move[1] = 3 + else + return false + fi + fi + fi ; + return true + end + + # Given a state, an a state value. Returns true iff the state has the given state value. + bool hasGivenStateValue(pair(pair, int) state, int stateValue) is + if state == null then + return false + else + int actual = snd state ; + return actual == stateValue + fi + end + + # Notify a move made by a human player to the AI. Return true. + bool notifyMoveAI(pair(pair, pair) board, char currentTurn, char playerSymbol, pair(pair, pair) aiData, int moveRow, int moveColumn) is + + #pair(char, pair) info = fst aiData ; #unused + pair(pair, int) stateTree = snd aiData ; + + pair(pair, pair) front = fst stateTree ; + + #pair(pair, pair) board = fst front ; #unused + pair(pair, pair) pointers = snd front ; + + println "AI is cleaning up its memory..." ; + + # Set new state tree, remove all other children created by making other moves. + snd aiData = call deleteAllOtherChildren(pointers, moveRow, moveColumn) ; + bool _ = call deleteThisStateOnly(stateTree) ; + return true + end + + # Delete all decendent states apart from those made by moving a given move. Return the child state of that given move. + pair(pair, int) deleteAllOtherChildren(pair(pair, pair) pointers, int moveRow, int moveColumn) is + pair(pair, pair) front = fst pointers ; + + pair(pair, pair) row1 = fst front ; + pair(pair, pair) row2 = snd front ; + pair(pair, pair) row3 = snd pointers ; + + # Find which row to keep or which rows to delete. + pair(pair, pair) toKeepRow = null; + pair(pair, pair) toDeleteRow1 = null; + pair(pair, pair) toDeleteRow2 = null; + + if moveRow == 1 then + toKeepRow = row1 ; + toDeleteRow1 = row2 ; + toDeleteRow2 = row3 + else + toDeleteRow1 = row1 ; + if moveRow == 2 then + toKeepRow = row2 ; + toDeleteRow2 = row3 + else + # moveRow == 3 + toKeepRow = row3 ; + toDeleteRow2 = row2 + fi + fi ; + + pair(pair, int) out = call deleteAllOtherChildrenRow(toKeepRow, moveColumn) ; + bool _ = call deleteChildrenStateRecursivelyRow(toDeleteRow1) ; + _ = call deleteChildrenStateRecursivelyRow(toDeleteRow2) ; + + return out + end + + pair(pair, int) deleteAllOtherChildrenRow(pair(pair, pair) rowPointers, int moveColumn) is + pair(pair, pair) front = fst rowPointers ; + + pair(pair, int) cell1 = fst front ; + pair(pair, int) cell2 = snd front ; + pair(pair, int) cell3 = snd rowPointers ; + + # Find which cell to keep or which cells to delete. + pair(pair, int) toKeepCell = null; + pair(pair, int) toDeleteCell1 = null; + pair(pair, int) toDeleteCell2 = null; + + if moveColumn == 1 then + toKeepCell = cell1 ; + toDeleteCell1 = cell2 ; + toDeleteCell2 = cell3 + else + toDeleteCell1 = cell1 ; + if moveColumn == 2 then + toKeepCell = cell2 ; + toDeleteCell2 = cell3 + else + # moveColumn == 3 + toKeepCell = cell3 ; + toDeleteCell2 = cell2 + fi + fi ; + + bool _ = call deleteStateTreeRecursively(toDeleteCell1) ; + _ = call deleteStateTreeRecursively(toDeleteCell2) ; + + return toKeepCell + end + + # Deallocate a given state and all its decendents. + bool deleteStateTreeRecursively(pair(pair, int) stateTree) is + if stateTree == null then + return true + else + pair(pair, pair) front = fst stateTree ; + + pair(pair, pair) board = fst front ; + pair(pair, pair) pointers = snd front ; + + bool _ = call deleteChildrenStateRecursively(pointers) ; + _ = call deleteThisStateOnly(stateTree) ; + return true + fi + end + + # Given a state tree, deallocate the board, the pointers and the other pairs of this state only. The childrens are preserved. Return true. + bool deleteThisStateOnly(pair(pair, int) stateTree) is + pair(pair, pair) front = fst stateTree ; + + pair(pair, pair) board = fst front ; + pair(pair, pair) pointers = snd front ; + + bool _ = call freeBoard(board) ; + _ = call freePointers(pointers) ; + free front ; + free stateTree ; + return true + end + + bool freePointers(pair(pair, pair) pointers) is + pair(pair, pair) front = fst pointers ; + + pair(pair, pair) row1 = fst front ; + pair(pair, pair) row2 = snd front ; + pair(pair, pair) row3 = snd pointers ; + + bool _ = call freePointersRow(row1) ; + _ = call freePointersRow(row2) ; + _ = call freePointersRow(row3) ; + + free front ; + free pointers ; + return true + end + + bool freePointersRow(pair(pair, pair) rowPointers) is + pair(pair, pair) front = fst rowPointers ; + + free front ; + free rowPointers ; + return true + end + + # Deallocate all decendent states. + bool deleteChildrenStateRecursively(pair(pair, pair) pointers) is + pair(pair, pair) front = fst pointers ; + + pair(pair, pair) row1 = fst front ; + pair(pair, pair) row2 = snd front ; + pair(pair, pair) row3 = snd pointers ; + + bool _ = call deleteChildrenStateRecursivelyRow(row1) ; + _ = call deleteChildrenStateRecursivelyRow(row2) ; + _ = call deleteChildrenStateRecursivelyRow(row3) ; + + return true + end + + # Deallocate all decendent states given a row of pointers. + bool deleteChildrenStateRecursivelyRow(pair(pair, pair) rowPointers) is + pair(pair, pair) front = fst rowPointers ; + pair(pair, int) cell1 = fst front ; + pair(pair, int) cell2 = snd front ; + pair(pair, int) cell3 = snd rowPointers ; + + bool _ = call deleteStateTreeRecursively(cell1) ; + _ = call deleteStateTreeRecursively(cell2) ; + _ = call deleteStateTreeRecursively(cell3) ; + + return true + end + + ############################### Game Engine Functions ################################## + + # Ask for a move from the current player. The valid move is stored in the move array. Return true. + bool askForAMove(pair(pair, pair) board, char currentTurn, char playerSymbol, pair(pair, pair) aiData, int[] move) is + if currentTurn == playerSymbol then + bool _ = call askForAMoveHuman(board, move) + else + bool _ = call askForAMoveAI(board, currentTurn, playerSymbol, aiData, move) + fi ; + return true + end + + # Place the given move of the currentTurn in the board. Return true. + bool placeMove(pair(pair, pair) board, char currentTurn, int moveRow, int moveColumn) is + + # Find the target row. + pair(pair, char) targetRow = null ; + if moveRow <= 2 then + pair(pair, pair) front = fst board ; + if moveRow == 1 then + targetRow = fst front + else + # moveRow == 2 + targetRow = snd front + fi + else + # moveRow == 3 + targetRow = snd board + fi ; + + # Set the target cell + if moveColumn <= 2 then + pair(char, char) front = fst targetRow ; + if moveColumn == 1 then + fst front = currentTurn + else + # moveColumn == 2 + snd front = currentTurn + fi + else + # moveColumn == 3 + snd targetRow = currentTurn + fi ; + return true + + end + + # Notify the opponent about a move of another party. Return true. + bool notifyMove(pair(pair, pair) board, char currentTurn, char playerSymbol, pair(pair, pair) aiData, int moveRow, int moveColumn) is + if currentTurn == playerSymbol then + bool _ = call notifyMoveAI(board, currentTurn, playerSymbol, aiData, moveRow, moveColumn) + else + bool _ = call notifyMoveHuman(board, currentTurn, playerSymbol, moveRow, moveColumn) + fi ; + return true + end + + # Given either 'x' or 'o', returns another one. + char oppositeSymbol(char symbol) is + if symbol == 'x' then + return 'o' + else + if symbol == 'o' then + return 'x' + else + # Should not happen! + println "Internal Error: symbol given is neither \'x\' or \'o\'" ; + exit -1 + fi + fi + end + + # row = 1, 2 or 3 + # column = 1, 2 or 3 + char symbolAt(pair(pair, pair) board, int row, int column) is + + # Find the target row. + pair(pair, char) targetRow = null ; + if row <= 2 then + pair(pair, pair) front = fst board ; + if row == 1 then + targetRow = fst front + else + # row == 2 + targetRow = snd front + fi + else + # row == 3 + targetRow = snd board + fi ; + + # Now find the target cell. + char targetCell = '\0' ; + if column <= 2 then + pair(char, char) front = fst targetRow ; + if column == 1 then + targetCell = fst front + else + # column == 2 + targetCell = snd front + fi + else + # column == 3 + targetCell = snd targetRow + fi ; + + return targetCell + end + + # Return true if there is at least one empty cell where the next player can place a move. Otherwise, return false (game ends). + bool containEmptyCell(pair(pair, pair) board) is + pair(pair, pair) front = fst board ; + + pair(pair, char) row1 = fst front ; + pair(pair, char) row2 = snd front ; + pair(pair, char) row3 = snd board ; + + bool row1ContainEmpty = call containEmptyCellRow(row1) ; + bool row2ContainEmpty = call containEmptyCellRow(row2) ; + bool row3ContainEmpty = call containEmptyCellRow(row3) ; + + return row1ContainEmpty || row2ContainEmpty || row3ContainEmpty + end + + bool containEmptyCellRow(pair(pair, char) row) is + pair(char, char) front = fst row ; + + char cell1 = fst front ; + char cell2 = snd front ; + char cell3 = snd row ; + + return cell1 == '\0' || cell2 == '\0' || cell3 == '\0' + end + + # Find if the candidate symbol ('x' or 'o') has won the game. + # Returns true if and only if it has won. + bool hasWon(pair(pair, pair) board, char candidate) is + char c11 = call symbolAt(board, 1, 1) ; + char c12 = call symbolAt(board, 1, 2) ; + char c13 = call symbolAt(board, 1, 3) ; + char c21 = call symbolAt(board, 2, 1) ; + char c22 = call symbolAt(board, 2, 2) ; + char c23 = call symbolAt(board, 2, 3) ; + char c31 = call symbolAt(board, 3, 1) ; + char c32 = call symbolAt(board, 3, 2) ; + char c33 = call symbolAt(board, 3, 3) ; + + return + # Row win + c11 == candidate && c12 == candidate && c13 == candidate || + c21 == candidate && c22 == candidate && c23 == candidate || + c31 == candidate && c32 == candidate && c33 == candidate || + + # Column win + c11 == candidate && c21 == candidate && c31 == candidate || + c12 == candidate && c22 == candidate && c32 == candidate || + c13 == candidate && c23 == candidate && c33 == candidate || + + # Diagonal win + c11 == candidate && c22 == candidate && c33 == candidate || + c13 == candidate && c22 == candidate && c31 == candidate + end + + # Allocate a new board. + # We use a Pair4Three structure to store pointers to the 3 rows. + pair(pair, pair) allocateNewBoard() is + pair(pair, char) row1 = call allocateNewRow() ; + pair(pair, char) row2 = call allocateNewRow() ; + pair(pair, char) row3 = call allocateNewRow() ; + + pair(pair, pair) front = newpair(row1, row2) ; + pair(pair, pair) root = newpair(front, row3) ; + return root + end + + # Allocate a row of the board. + # A row is represented by a Pair4Three structure. + # The default value in each cell is '\0'. + pair(pair, char) allocateNewRow() is + pair(char, char) front = newpair('\0', '\0') ; + pair(pair, char) root = newpair(front, '\0') ; + return root + end + + # Free a memory used to store the whole board. + # Return true. + bool freeBoard(pair(pair, pair) board) is + pair(pair, pair) front = fst board ; + + pair(pair, char) row1 = fst front ; + pair(pair, char) row2 = snd front ; + pair(pair, char) row3 = snd board ; + + bool _ = call freeRow(row1) ; + _ = call freeRow(row2) ; + _ = call freeRow(row3) ; + + free front ; + free board ; + return true + end + + # Free the memory used for a row. Return true. + bool freeRow(pair(pair, char) row) is + pair(char, char) front = fst row ; + free front ; + free row ; + return true + end + + # For debugging purpose. + bool printAiData(pair(pair, pair) aiData) is + + pair(char, pair) info = fst aiData ; + pair(pair, int) stateTree = snd aiData ; + + bool _ = call printStateTreeRecursively(stateTree) ; + exit 0 + end + + bool printStateTreeRecursively(pair(pair, int) stateTree) is + if stateTree == null then + return true + else + pair(pair, pair) front = fst stateTree ; + + pair(pair, pair) board = fst front ; + pair(pair, pair) pointers = snd front ; + int value = snd stateTree ; + + # Print the value + print 'v' ; + print '=' ; + println value ; + + bool _ = call printBoard(board) ; + _ = call printChildrenStateTree(pointers) ; + + println 'p' ; + return true + fi + end + + bool printChildrenStateTree(pair(pair, pair) pointers) is + pair(pair, pair) front = fst pointers ; + + pair(pair, pair) row1 = fst front ; + pair(pair, pair) row2 = snd front ; + pair(pair, pair) row3 = snd pointers ; + + bool _ = call printChildrenStateTreeRow(row1) ; + _ = call printChildrenStateTreeRow(row2) ; + _ = call printChildrenStateTreeRow(row3) ; + return true + end + + bool printChildrenStateTreeRow(pair(pair, pair) rowPointers) is + pair(pair, pair) front = fst rowPointers ; + + pair(pair, int) cell1 = fst front ; + pair(pair, int) cell2 = snd front ; + pair(pair, int) cell3 = snd rowPointers ; + + bool _ = call printStateTreeRecursively(cell1) ; + _ = call printStateTreeRecursively(cell2) ; + _ = call printStateTreeRecursively(cell3) ; + + return true + end + + ############################## Main Function ############################ + + char playerSymbol = call chooseSymbol() ; + char aiSymbol = call oppositeSymbol(playerSymbol) ; + char currentTurn = 'x' ; + + pair(pair, pair) board = call allocateNewBoard() ; + + println "Initialising AI. Please wait, this may take a few minutes." ; + pair(pair, pair) aiData = call initAI(aiSymbol) ; + + int turnCount = 0 ; + char winner = '\0' ; + + bool _ = call printBoard(board) ; + + while winner == '\0' && turnCount < 9 do + int[] move = [0, 0] ; + _ = call askForAMove(board, currentTurn, playerSymbol, aiData, move) ; + _ = call placeMove(board, currentTurn, move[0], move[1]) ; + _ = call notifyMove(board, currentTurn, playerSymbol, aiData, move[0], move[1]) ; + _ = call printBoard(board) ; + bool won = call hasWon(board, currentTurn) ; + if won then + winner = currentTurn + else + skip + fi ; + + # Progress to the next turn + currentTurn = call oppositeSymbol(currentTurn) ; + turnCount = turnCount + 1 + done ; + + _ = call freeBoard(board) ; + _ = call destroyAI(aiData) ; + + if winner != '\0' then + print winner ; + println " has won!" + else + println "Stalemate!" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/array/array.wacc b/src/test/wacc/waccPrograms/valid/array/array.wacc new file mode 100644 index 0000000..099415b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/array.wacc @@ -0,0 +1,32 @@ +# moderate complexity array manipulations + +# Output: +# #addrs# = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} +# + +# Program: + +begin + int[] a = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ; + int i = 0 ; + while i < 10 + do + a[i] = i ; + i = i + 1 + done ; + print a ; + print " = {" ; + i = 0 ; + while i < 10 + do + print a[i] ; + if i < 9 + then + print ", " + else + skip + fi ; + i = i + 1 + done ; + println "}" +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayBasic.wacc b/src/test/wacc/waccPrograms/valid/array/arrayBasic.wacc new file mode 100644 index 0000000..e271e80 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayBasic.wacc @@ -0,0 +1,9 @@ +# basic array declaration and assignment + +# Output: + +# Program: + +begin + int[] a = [0] +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayEmpty.wacc b/src/test/wacc/waccPrograms/valid/array/arrayEmpty.wacc new file mode 100644 index 0000000..286f5a0 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayEmpty.wacc @@ -0,0 +1,9 @@ +# empty array declaration + +# Output: + +# Program: + +begin + int[] a = [] +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayIndexMayBeArrayIndex.wacc b/src/test/wacc/waccPrograms/valid/array/arrayIndexMayBeArrayIndex.wacc new file mode 100644 index 0000000..f156bc3 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayIndexMayBeArrayIndex.wacc @@ -0,0 +1,24 @@ +# Testing recursive array indexing + +# Output: +# 6 +# 7 +# 8 +# + +# Program: + +begin + int[] idxs1 = [2, 0, 1] ; + int[] idxs2 = [1, 2, 0] ; + # idxs1[idxs2[0]] = 0 + # idxs1[idxs2[1]] = 1 + # idxs1[idxs2[2]] = 2 + int[] xs = [5, 6, 7] ; + int i = 0 ; + while i != 3 do + xs[idxs1[idxs2[i]]] = xs[idxs1[idxs2[i]]] + 1 ; + println (xs[idxs1[idxs2[i]]]) ; + i = i + 1 + done +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayLength.wacc b/src/test/wacc/waccPrograms/valid/array/arrayLength.wacc new file mode 100644 index 0000000..422309f --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayLength.wacc @@ -0,0 +1,12 @@ +# check length of array + +# Output: +# 4 +# + +# Program: + +begin + int[] a = [43, 2, 18, 1] ; + println len a +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayLookup.wacc b/src/test/wacc/waccPrograms/valid/array/arrayLookup.wacc new file mode 100644 index 0000000..a5be3fa --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayLookup.wacc @@ -0,0 +1,12 @@ +# check first element of array + +# Output: +# 43 +# + +# Program: + +begin + int[] a = [43, 2, 18, 1] ; + println a[0] +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayNested.wacc b/src/test/wacc/waccPrograms/valid/array/arrayNested.wacc new file mode 100644 index 0000000..fe3ef0c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayNested.wacc @@ -0,0 +1,16 @@ +# basic array declaration and assignment + +# Output: +# 3 +# 3 +# + +# Program: + +begin + int[] a = [1,2,3]; + int[] b = [3,4]; + int[][] c = [a,b] ; + println c[0][2] ; + println c[1][0] +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayOnHeap.wacc b/src/test/wacc/waccPrograms/valid/array/arrayOnHeap.wacc new file mode 100644 index 0000000..f4fba21 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayOnHeap.wacc @@ -0,0 +1,26 @@ +# ensures that arrays are heap allocated + +# Output: +# false +# 0 +# 1 +# + +# Program: + +begin + int[] dummy_arr = [] ; + int[][] arrs = [dummy_arr, dummy_arr] ; + int i = 0; + while i < 2 do + int[] arr = [i] ; + arrs[i] = arr ; + i = i + 1 + done; + + # should have different addresses + println arrs[0] == arrs[1]; + # should have different values + println arrs[0][0]; + println arrs[1][0] +end diff --git a/src/test/wacc/waccPrograms/valid/array/arrayPrint.wacc b/src/test/wacc/waccPrograms/valid/array/arrayPrint.wacc new file mode 100644 index 0000000..63630c1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arrayPrint.wacc @@ -0,0 +1,27 @@ +# print the contents of a simple array + +# Output: +# #addrs# = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} +# + +# Program: + +begin + int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] ; + int i = 0 ; + print a ; + print " = {" ; + i = 0 ; + while i < 10 + do + print a[i] ; + if i < 9 + then + print ", " + else + skip + fi ; + i = i + 1 + done ; + println "}" +end diff --git a/src/test/wacc/waccPrograms/valid/array/arraySimple.wacc b/src/test/wacc/waccPrograms/valid/array/arraySimple.wacc new file mode 100644 index 0000000..e896702 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/arraySimple.wacc @@ -0,0 +1,13 @@ +# simple array assignment and lookup + +# Output: +# 42 +# + +# Program: + +begin + int[] a = [0] ; + a[0] = 42 ; + println a[0] +end diff --git a/src/test/wacc/waccPrograms/valid/array/charArrayInStringArray.wacc b/src/test/wacc/waccPrograms/valid/array/charArrayInStringArray.wacc new file mode 100644 index 0000000..bae8494 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/charArrayInStringArray.wacc @@ -0,0 +1,11 @@ +# a char[] can fit into a string[] +# Thanks to Iurii Zamiatin for finding this + +# Output: + +# Program: + +begin + char[] testArr = ['a', 'b', 'c']; + string[] arr = [testArr, "box", "foo", "bar"] +end diff --git a/src/test/wacc/waccPrograms/valid/array/emptyArrayAloneIsFine.wacc b/src/test/wacc/waccPrograms/valid/array/emptyArrayAloneIsFine.wacc new file mode 100644 index 0000000..c5987bf --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/emptyArrayAloneIsFine.wacc @@ -0,0 +1,9 @@ +# this is fine + +# Output: + +# Program: + +begin + int[] x = [] +end diff --git a/src/test/wacc/waccPrograms/valid/array/emptyArrayNextLine.wacc b/src/test/wacc/waccPrograms/valid/array/emptyArrayNextLine.wacc new file mode 100644 index 0000000..00d155f --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/emptyArrayNextLine.wacc @@ -0,0 +1,10 @@ +# This should work just fine + +# Output: + +# Program: + +begin + int[] x = [] ; + bool y = true +end diff --git a/src/test/wacc/waccPrograms/valid/array/emptyArrayPrint.wacc b/src/test/wacc/waccPrograms/valid/array/emptyArrayPrint.wacc new file mode 100644 index 0000000..ce12654 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/emptyArrayPrint.wacc @@ -0,0 +1,12 @@ +# basic array declaration and print after + +# Output: +# true +# + +# Program: + +begin + int[] x = [] ; + println true +end diff --git a/src/test/wacc/waccPrograms/valid/array/emptyArrayReplace.wacc b/src/test/wacc/waccPrograms/valid/array/emptyArrayReplace.wacc new file mode 100644 index 0000000..a97b077 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/emptyArrayReplace.wacc @@ -0,0 +1,13 @@ +# it should be possible to reassign to an array + +# Output: +# true +# + +# Program: + +begin + int[] x = [] ; + x = [0] ; + println true +end diff --git a/src/test/wacc/waccPrograms/valid/array/emptyArrayScope.wacc b/src/test/wacc/waccPrograms/valid/array/emptyArrayScope.wacc new file mode 100644 index 0000000..c38efa7 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/emptyArrayScope.wacc @@ -0,0 +1,12 @@ +# Scoping shouldn't affect arrays + +# Output: + +# Program: + +begin + int[] x = [] ; + begin + bool y = true + end +end diff --git a/src/test/wacc/waccPrograms/valid/array/freeArray.wacc b/src/test/wacc/waccPrograms/valid/array/freeArray.wacc new file mode 100644 index 0000000..3f98686 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/freeArray.wacc @@ -0,0 +1,12 @@ +# Create and free an array + +# Output: + +# Program: + +begin + int[] arr = [1, 2, 3] ; + free arr +end + + diff --git a/src/test/wacc/waccPrograms/valid/array/lenArrayIndex.wacc b/src/test/wacc/waccPrograms/valid/array/lenArrayIndex.wacc new file mode 100644 index 0000000..dd53f99 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/lenArrayIndex.wacc @@ -0,0 +1,13 @@ +# Tests that array length works on array indexes +# Thanks to Nixon Enraght-Moony for his help in this test-case + +# Output: +# 0 + +# Program: + +begin + int[] arr = []; + int[][] arrs = [arr]; + print len arrs[0] +end diff --git a/src/test/wacc/waccPrograms/valid/array/modifyString.wacc b/src/test/wacc/waccPrograms/valid/array/modifyString.wacc new file mode 100644 index 0000000..91194d9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/modifyString.wacc @@ -0,0 +1,18 @@ +# create and modify string as array of characters + +# Output: +# hello world! +# Hello world! +# Hi! +# + +# Program: + +begin + char[] str = ['h','e','l','l','o',' ','w','o','r','l','d','!'] ; + println str ; + str[0] = 'H' ; + println str ; + str = ['H','i','!'] ; + println str +end diff --git a/src/test/wacc/waccPrograms/valid/array/printRef.wacc b/src/test/wacc/waccPrograms/valid/array/printRef.wacc new file mode 100644 index 0000000..9b4ed7c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/printRef.wacc @@ -0,0 +1,13 @@ +# basic array (reference) printing + +# Output: +# Printing an array variable gives an address, such as #addrs# +# + +# Program: + +begin + print "Printing an array variable gives an address, such as " ; + int[] a = [1,2,3] ; + println a +end diff --git a/src/test/wacc/waccPrograms/valid/array/readIntoArray.wacc b/src/test/wacc/waccPrograms/valid/array/readIntoArray.wacc new file mode 100644 index 0000000..06c0ca9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/readIntoArray.wacc @@ -0,0 +1,17 @@ +# Read into an array. + +# Input: 10 + +# Output: +# 1 +# 10 +# + +# Program: + +begin + int[] nums = [1, 2, 3]; + println nums[0]; + read nums[0]; + println nums[0] +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/array/stringFromArray.wacc b/src/test/wacc/waccPrograms/valid/array/stringFromArray.wacc new file mode 100644 index 0000000..1743cca --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/array/stringFromArray.wacc @@ -0,0 +1,10 @@ +# char[] can be directly assigned to string + +# Output: + +# Program: + +begin + string a = ['a', 'b'] ; + exit 0 +end diff --git a/src/test/wacc/waccPrograms/valid/basic/exit/exit-1.wacc b/src/test/wacc/waccPrograms/valid/basic/exit/exit-1.wacc new file mode 100644 index 0000000..09c36e9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/exit/exit-1.wacc @@ -0,0 +1,12 @@ +# common error exit statement + +# Output: + +# Exit: +# 255 + +# Program: + +begin + exit -1 +end diff --git a/src/test/wacc/waccPrograms/valid/basic/exit/exitBasic.wacc b/src/test/wacc/waccPrograms/valid/basic/exit/exitBasic.wacc new file mode 100644 index 0000000..bd25473 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/exit/exitBasic.wacc @@ -0,0 +1,12 @@ +# basic exit statement + +# Output: + +# Exit: +# 7 + +# Program: + +begin + exit 7 +end diff --git a/src/test/wacc/waccPrograms/valid/basic/exit/exitBasic2.wacc b/src/test/wacc/waccPrograms/valid/basic/exit/exitBasic2.wacc new file mode 100644 index 0000000..b855b32 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/exit/exitBasic2.wacc @@ -0,0 +1,12 @@ +# basic exit statement + +# Output: + +# Exit: +# 42 + +# Program: + +begin + exit 42 +end diff --git a/src/test/wacc/waccPrograms/valid/basic/exit/exitWrap.wacc b/src/test/wacc/waccPrograms/valid/basic/exit/exitWrap.wacc new file mode 100644 index 0000000..78c1b06 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/exit/exitWrap.wacc @@ -0,0 +1,12 @@ +# exit with wrapped int + +# Output: + +# Exit: +# 0 + +# Program: + +begin + exit 256 +end diff --git a/src/test/wacc/waccPrograms/valid/basic/skip/comment.wacc b/src/test/wacc/waccPrograms/valid/basic/skip/comment.wacc new file mode 100644 index 0000000..40ff174 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/skip/comment.wacc @@ -0,0 +1,10 @@ +# simple skip program with comment line + +# Output: + +# Program: + +begin + # I can write stuff on a comment line + skip +end diff --git a/src/test/wacc/waccPrograms/valid/basic/skip/commentEoF.wacc b/src/test/wacc/waccPrograms/valid/basic/skip/commentEoF.wacc new file mode 100755 index 0000000..b8c936d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/skip/commentEoF.wacc @@ -0,0 +1,10 @@ +# simple skip program with comment line ended by EoF not EoL + +# Output: + +# Program: + +begin + skip +end +# I can end a file with a comment \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/basic/skip/commentInLine.wacc b/src/test/wacc/waccPrograms/valid/basic/skip/commentInLine.wacc new file mode 100644 index 0000000..de24d8d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/skip/commentInLine.wacc @@ -0,0 +1,9 @@ +# simple skip program with in-line comment + +# Output: + +# Program: + +begin + skip # I can write comments in-line +end diff --git a/src/test/wacc/waccPrograms/valid/basic/skip/skip.wacc b/src/test/wacc/waccPrograms/valid/basic/skip/skip.wacc new file mode 100644 index 0000000..29945a6 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/basic/skip/skip.wacc @@ -0,0 +1,7 @@ +# simple skip program + +# Output: + +# Program: + +begin skip end diff --git a/src/test/wacc/waccPrograms/valid/expressions/andExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/andExpr.wacc new file mode 100644 index 0000000..17d2a52 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/andExpr.wacc @@ -0,0 +1,17 @@ +# evaluating and + +# Output: +# false +# true +# false +# + +# Program: + +begin + bool a = true ; + bool b = false ; + println a && b ; + println a && true ; + println b && false +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/andOverOrExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/andOverOrExpr.wacc new file mode 100644 index 0000000..4e55226 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/andOverOrExpr.wacc @@ -0,0 +1,16 @@ +# evaluating boolean operator precedence + +# Output: +# true +# false +# + +# Program: + +begin + bool a = false ; + bool b = false ; + bool c = true ; + println a && b || c ; + println a && (b || c) +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/boolCalc.wacc b/src/test/wacc/waccPrograms/valid/expressions/boolCalc.wacc new file mode 100644 index 0000000..e5b5f8f --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/boolCalc.wacc @@ -0,0 +1,14 @@ +# simple boolean calculation + +# Output: +# false +# + +# Program: + +begin + bool b1 = true ; + bool b2 = false ; + bool b3 = b1 && b2 ; + println b3 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/boolExpr1.wacc b/src/test/wacc/waccPrograms/valid/expressions/boolExpr1.wacc new file mode 100644 index 0000000..25bb123 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/boolExpr1.wacc @@ -0,0 +1,16 @@ +# evaluating a moderately complex boolean expression + +# Output: +# Correct +# + +# Program: + +begin + bool b = ! ( ( true && false) || (true && false) ); + if b == true then + println "Correct" + else + println "Wrong" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/charComparisonExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/charComparisonExpr.wacc new file mode 100644 index 0000000..ede77ba --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/charComparisonExpr.wacc @@ -0,0 +1,23 @@ +# detailed battery of character comparison tests + +# Output: +# false +# true +# true +# true +# false +# false +# + +# Program: + +begin + char c1 = 'a' ; + char c2 = 'z' ; + println c1 == c2 ; + println c1 != c2 ; + println c1 < c2 ; + println c1 <= c2 ; + println c1 > c2 ; + println c1 >= c2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/divExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/divExpr.wacc new file mode 100644 index 0000000..1352341 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/divExpr.wacc @@ -0,0 +1,13 @@ +# evaluating integer division + +# Output: +# 1 +# + +# Program: + +begin + int x = 5 ; + int y = 3 ; + println x / y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/equalsExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/equalsExpr.wacc new file mode 100644 index 0000000..e86fd23 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/equalsExpr.wacc @@ -0,0 +1,19 @@ +# evaluating equality + +# Output: +# false +# false +# true +# + +# Program: + +begin + int x = 2 ; + int y = 4 ; + int z = 4 ; + bool b = x == y ; + println b ; + println x == y ; + println y == z +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/equalsOverAnd.wacc b/src/test/wacc/waccPrograms/valid/expressions/equalsOverAnd.wacc new file mode 100644 index 0000000..6e5faef --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/equalsOverAnd.wacc @@ -0,0 +1,16 @@ +# evaluating equality and boolean operator precedence + +# Output: +# false +# true +# + +# Program: + +begin + bool p = false ; + bool q = true ; + bool r = false ; + println p == q && r ; + println p == (q && r) +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/equalsOverBool.wacc b/src/test/wacc/waccPrograms/valid/expressions/equalsOverBool.wacc new file mode 100644 index 0000000..4ae323f --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/equalsOverBool.wacc @@ -0,0 +1,17 @@ +# evaluating equality and boolean operator precedence + +# Output: +# true +# false +# + +# Program: + +begin + bool p = true ; + bool q = true ; + bool r = false ; + bool s = true ; + println p && q != r || s ; + println (p && q) != (r || s) +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/equalsOverOr.wacc b/src/test/wacc/waccPrograms/valid/expressions/equalsOverOr.wacc new file mode 100644 index 0000000..620b9d8 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/equalsOverOr.wacc @@ -0,0 +1,16 @@ +# evaluating equality and boolean operator precedence + +# Output: +# true +# false +# + +# Program: + +begin + bool p = false ; + bool q = true ; + bool r = true ; + println p == q || r ; + println p == (q || r) +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/greaterEqExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/greaterEqExpr.wacc new file mode 100644 index 0000000..67c0133 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/greaterEqExpr.wacc @@ -0,0 +1,19 @@ +# evaluating greater-than + +# Output: +# false +# true +# true +# + +# Program: + +begin + int x = 2 ; + int y = 6 ; + int z = 4 ; + int a = 4 ; + println x >= y ; + println y >= z ; + println z >= z +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/greaterExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/greaterExpr.wacc new file mode 100644 index 0000000..2f0fc3e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/greaterExpr.wacc @@ -0,0 +1,16 @@ +# evaluating greater-than + +# Output: +# false +# true +# + +# Program: + +begin + int x = 2 ; + int y = 6 ; + int z = 4 ; + println x > y ; + println y > z +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/ifExpr1.wacc b/src/test/wacc/waccPrograms/valid/expressions/ifExpr1.wacc new file mode 100644 index 0000000..3ce877c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ifExpr1.wacc @@ -0,0 +1,11 @@ +# testing if compiler can handle if expressions + +# Output: +# a +# + +# Program: + +begin + println (if 0 < 1 then 'a' else 'b' fi) +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/ifExpr2.wacc b/src/test/wacc/waccPrograms/valid/expressions/ifExpr2.wacc new file mode 100644 index 0000000..ae46206 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ifExpr2.wacc @@ -0,0 +1,13 @@ +# testing if compiler can handle if expressions + +# Output: +# 25 +# + +# Program: + +begin + bool cond = false ; + int x = if cond then 10 else 25 fi ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/ifExpr3.wacc b/src/test/wacc/waccPrograms/valid/expressions/ifExpr3.wacc new file mode 100644 index 0000000..4633b95 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ifExpr3.wacc @@ -0,0 +1,15 @@ +# testing if compiler can handle if expressions + +# Output: +# 10 +# + +# Program: + +begin + pair(int, int) a = newpair(10, 25) ; + int b = fst a ; + int c = snd a ; + println if b > c then c else b fi +end + diff --git a/src/test/wacc/waccPrograms/valid/expressions/ifExpr4.wacc b/src/test/wacc/waccPrograms/valid/expressions/ifExpr4.wacc new file mode 100644 index 0000000..5f070b4 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ifExpr4.wacc @@ -0,0 +1,16 @@ +# testing if compiler can handle if expressions + +# Output: +# 100 +# + +# Program: + +begin + int a = 0 ; + while a < 100 do + a = if true then a + 1 else 1001 fi + done ; + println a +end + diff --git a/src/test/wacc/waccPrograms/valid/expressions/ifExpr5.wacc b/src/test/wacc/waccPrograms/valid/expressions/ifExpr5.wacc new file mode 100644 index 0000000..9c46f37 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ifExpr5.wacc @@ -0,0 +1,13 @@ +# testing if compiler can handle if expressions + +# Output: +# 40 +# + +# Program: + +begin + int a = 10 + if true then 30 else -5 fi; + println a +end + diff --git a/src/test/wacc/waccPrograms/valid/expressions/ifExpr6.wacc b/src/test/wacc/waccPrograms/valid/expressions/ifExpr6.wacc new file mode 100644 index 0000000..9e405c8 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ifExpr6.wacc @@ -0,0 +1,13 @@ +# testing if compiler can handle if expressions + +# Output: +# 20 +# + +# Program: + +begin + int a = if true then if false then 10 else 20 fi else 30 fi; + println a +end + diff --git a/src/test/wacc/waccPrograms/valid/expressions/ifExpr7.wacc b/src/test/wacc/waccPrograms/valid/expressions/ifExpr7.wacc new file mode 100644 index 0000000..927ffbf --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ifExpr7.wacc @@ -0,0 +1,13 @@ +# testing if compiler can handle if expressions + +# Output: +# hello +# + +# Program: + +begin + string a = if false then "bye" else "hello" fi; + println a +end + diff --git a/src/test/wacc/waccPrograms/valid/expressions/intCalc.wacc b/src/test/wacc/waccPrograms/valid/expressions/intCalc.wacc new file mode 100644 index 0000000..cc08d42 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/intCalc.wacc @@ -0,0 +1,14 @@ +# simple integer calculation + +# Output: +# 72 +# + +# Program: + +begin + int x = 42 ; + int y = 30 ; + int z = x + y ; + println z +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/intExpr1.wacc b/src/test/wacc/waccPrograms/valid/expressions/intExpr1.wacc new file mode 100644 index 0000000..54c0637 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/intExpr1.wacc @@ -0,0 +1,16 @@ +# evaluating a moderately complex integer expression + +# Output: +# Correct +# + +# Program: + +begin + int a = ( 10 * 1 + 2 * 15 ); + if a == 40 then + println "Correct" + else + println "Wrong" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/lessCharExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/lessCharExpr.wacc new file mode 100644 index 0000000..49df2e5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/lessCharExpr.wacc @@ -0,0 +1,16 @@ +# evaluating less-than on characters + +# Output: +# true +# false +# + +# Program: + +begin + char a = 'a' ; + char e = 'e' ; + char c = 'c' ; + println a < e ; + println e < c +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/lessEqExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/lessEqExpr.wacc new file mode 100644 index 0000000..04cb4b9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/lessEqExpr.wacc @@ -0,0 +1,19 @@ +# evaluating less-than-or-equal-to + +# Output: +# true +# false +# true +# + +# Program: + +begin + int x = 2 ; + int y = 6 ; + int z = 4 ; + int a = 4 ; + println x <= y ; + println y <= z ; + println z <= a +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/lessExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/lessExpr.wacc new file mode 100644 index 0000000..b385d63 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/lessExpr.wacc @@ -0,0 +1,16 @@ +# evaluating less-than + +# Output: +# true +# false +# + +# Program: + +begin + int x = 2 ; + int y = 6 ; + int z = 4 ; + println x < y ; + println y < z +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/longExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/longExpr.wacc new file mode 100644 index 0000000..d4fb99a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/longExpr.wacc @@ -0,0 +1,13 @@ +# tests whether the compiler can handle long expressions + +# Output: + +# Exit: +# 153 + +begin + + int x = 1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + (9 + (10 + (11 + (12 + (13 + (14 + (15 + (16 + 17))))))))))))))); + exit x + +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/longExpr2.wacc b/src/test/wacc/waccPrograms/valid/expressions/longExpr2.wacc new file mode 100644 index 0000000..9d98dbe --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/longExpr2.wacc @@ -0,0 +1,13 @@ +# tests whether the compiler can handle long expressions + +# Output: + +# Exit: +# 10 + +begin + + int x = (2 + 3 + 2 + 1 + 1 + 1) - (1 + 2) * (3 - 4 / 6) / ( 2 * (18 - 17) + (3 * 4 / 4 + 6)); + exit x + +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/longExpr3.wacc b/src/test/wacc/waccPrograms/valid/expressions/longExpr3.wacc new file mode 100644 index 0000000..d8c68b9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/longExpr3.wacc @@ -0,0 +1,13 @@ +# tests whether the compiler can handle long expressions + +# Output: + +# Exit: +# 9 + +begin + + int x = ((((((((((((((((1 - 2) + 3) - 4) + 5) - 6) + 7) - 8) + 9) - 10) + 11) - 12) + 13) - 14) + 15) - 16) + 17); + exit x + +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/longSplitExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/longSplitExpr.wacc new file mode 100644 index 0000000..015c171 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/longSplitExpr.wacc @@ -0,0 +1,21 @@ +# tests whether the compiler can handle long expressions with several variables + +# Output: + +# Exit: +# 153 + +begin + + int a = 1 + 2 ; + int b = 3 + 4 ; + int c = 5 + 6 ; + int d = 7 + 8 ; + int e = 9 + 10 ; + int f = 11 + 12 ; + int g = 13 + 14 ; + int h = 15 + 16 ; + int i = 17 ; + exit a + b + c + d + e + f + g + h + i + +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/longSplitExpr2.wacc b/src/test/wacc/waccPrograms/valid/expressions/longSplitExpr2.wacc new file mode 100644 index 0000000..0b3a50d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/longSplitExpr2.wacc @@ -0,0 +1,21 @@ +# tests whether the compiler can handle long expressions with several variables + +# Output: +# 362880 +# 128 +# + +# Exit: +# 128 + +begin + + int x = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 ; + int y = -1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - 15 - 16 - 17 ; + int z = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10 ; + int div = 10 ; + println x + y + ( z / div ) ; + println (x + y + ( z / div )) % 256 ; + exit x + y + ( z / div ) + +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/minusExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/minusExpr.wacc new file mode 100644 index 0000000..448b524 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/minusExpr.wacc @@ -0,0 +1,13 @@ +# evaluating subtraction + +# Output: +# 5 +# + +# Program: + +begin + int x = 15 ; + int y = 20 ; + println y - x +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/minusMinusExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/minusMinusExpr.wacc new file mode 100644 index 0000000..138f44d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/minusMinusExpr.wacc @@ -0,0 +1,11 @@ +# -- should be recognised as two separate symbols + +# Output: +# 3 +# + +# Program: + +begin + println 1--2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/minusNoWhitespaceExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/minusNoWhitespaceExpr.wacc new file mode 100644 index 0000000..e6f6dd4 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/minusNoWhitespaceExpr.wacc @@ -0,0 +1,11 @@ +# subtraction expressions should not be whitespace sensitive + +# Output: +# -1 +# + +# Program: + +begin + println 1-2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/minusPlusExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/minusPlusExpr.wacc new file mode 100644 index 0000000..4475b49 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/minusPlusExpr.wacc @@ -0,0 +1,11 @@ +# -+ should be recognised as two separate symbols + +# Output: +# -1 +# + +# Program: + +begin + println 1-+2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/modExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/modExpr.wacc new file mode 100644 index 0000000..26d980d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/modExpr.wacc @@ -0,0 +1,13 @@ +# evaluating modulus + +# Output: +# 2 +# + +# Program: + +begin + int x = 5 ; + int y = 3 ; + println x % y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/multExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/multExpr.wacc new file mode 100644 index 0000000..93940b5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/multExpr.wacc @@ -0,0 +1,13 @@ +# evaluating multiplication + +# Output: +# 15 +# + +# Program: + +begin + int x = 5 ; + int y = 3 ; + println x * y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/multNoWhitespaceExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/multNoWhitespaceExpr.wacc new file mode 100644 index 0000000..0601832 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/multNoWhitespaceExpr.wacc @@ -0,0 +1,11 @@ +# multiplication expressions should not be whitespace sensitive + +# Output: +# 2 +# + +# Program: + +begin + println 1*2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/negBothDiv.wacc b/src/test/wacc/waccPrograms/valid/expressions/negBothDiv.wacc new file mode 100644 index 0000000..4ea6b35 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/negBothDiv.wacc @@ -0,0 +1,13 @@ +# division of a negative number by a negative number + +# Output: +# 2 +# + +# Program: + +begin + int x = -4 ; + int y = -2 ; + println x / y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/negBothMod.wacc b/src/test/wacc/waccPrograms/valid/expressions/negBothMod.wacc new file mode 100644 index 0000000..876aafc --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/negBothMod.wacc @@ -0,0 +1,13 @@ +# modulus of a negative number by a negative number + +# Output: +# -2 +# + +# Program: + +begin + int x = -5 ; + int y = -3 ; + println x % y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/negDividendDiv.wacc b/src/test/wacc/waccPrograms/valid/expressions/negDividendDiv.wacc new file mode 100644 index 0000000..27b0832 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/negDividendDiv.wacc @@ -0,0 +1,13 @@ +# division of a negative number + +# Output: +# -2 +# + +# Program: + +begin + int x = -4 ; + int y = 2 ; + println x / y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/negDividendMod.wacc b/src/test/wacc/waccPrograms/valid/expressions/negDividendMod.wacc new file mode 100644 index 0000000..10f0b1a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/negDividendMod.wacc @@ -0,0 +1,13 @@ +# modulus of a negative number + +# Output: +# -2 +# + +# Program: + +begin + int x = -5 ; + int y = 3 ; + println x % y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/negDivisorDiv.wacc b/src/test/wacc/waccPrograms/valid/expressions/negDivisorDiv.wacc new file mode 100644 index 0000000..a5eb095 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/negDivisorDiv.wacc @@ -0,0 +1,13 @@ +# division by a negative number + +# Output: +# -2 +# + +# Program: + +begin + int x = 4 ; + int y = -2 ; + println x / y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/negDivisorMod.wacc b/src/test/wacc/waccPrograms/valid/expressions/negDivisorMod.wacc new file mode 100644 index 0000000..21e49f1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/negDivisorMod.wacc @@ -0,0 +1,13 @@ +# modulus by a negative number + +# Output: +# 2 +# + +# Program: + +begin + int x = 5 ; + int y = -3 ; + println x % y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/negExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/negExpr.wacc new file mode 100644 index 0000000..0afa36b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/negExpr.wacc @@ -0,0 +1,12 @@ +# evaluating negation + +# Output: +# -42 +# + +# Program: + +begin + int x = 42 ; + println -x +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/notExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/notExpr.wacc new file mode 100644 index 0000000..e9fd9f4 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/notExpr.wacc @@ -0,0 +1,15 @@ +# evaluating not + +# Output: +# false +# true +# + +# Program: + +begin + bool a = true ; + bool b = false ; + println !a ; + println !b +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/notequalsExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/notequalsExpr.wacc new file mode 100644 index 0000000..1c2cb5c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/notequalsExpr.wacc @@ -0,0 +1,19 @@ +# evaluating inequality + +# Output: +# true +# true +# false +# + +# Program: + +begin + int x = 2 ; + int y = 4 ; + int z = 4 ; + bool b = x != y ; + println b ; + println x != y ; + println y != z +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/orExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/orExpr.wacc new file mode 100644 index 0000000..d1201cf --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/orExpr.wacc @@ -0,0 +1,17 @@ +# evaluating or + +# Output: +# true +# true +# false +# + +# Program: + +begin + bool a = true ; + bool b = false ; + println a || b ; + println a || true ; + println b || false +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/ordAndchrExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/ordAndchrExpr.wacc new file mode 100644 index 0000000..61a721e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/ordAndchrExpr.wacc @@ -0,0 +1,21 @@ +# evalutaing ord and chr + +# Output: +# a is 97 +# 99 is c +# + +# Program: + +begin + char a = 'a' ; + int i = 99 ; + + print a ; + print " is " ; + println ord a ; + + print i ; + print " is " ; + println chr i +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/plusExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/plusExpr.wacc new file mode 100644 index 0000000..e15c7ab --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/plusExpr.wacc @@ -0,0 +1,13 @@ +# evaluating addition + +# Output: +# 35 +# + +# Program: + +begin + int x = 15 ; + int y = 20 ; + println x + y +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/plusMinusExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/plusMinusExpr.wacc new file mode 100644 index 0000000..0522838 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/plusMinusExpr.wacc @@ -0,0 +1,11 @@ +# +- should be recognised as two separate symbols + +# Output: +# -1 +# + +# Program: + +begin + println 1+-2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/plusNoWhitespaceExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/plusNoWhitespaceExpr.wacc new file mode 100644 index 0000000..6b76389 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/plusNoWhitespaceExpr.wacc @@ -0,0 +1,11 @@ +# addition expressions should not be whitespace sensitive + +# Output: +# 3 +# + +# Program: + +begin + println 1+2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/plusPlusExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/plusPlusExpr.wacc new file mode 100644 index 0000000..339bc29 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/plusPlusExpr.wacc @@ -0,0 +1,11 @@ +# ++ should be recognised as two separate symbols + +# Output: +# 3 +# + +# Program: + +begin + println 1++2 +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/sequentialCount.wacc b/src/test/wacc/waccPrograms/valid/expressions/sequentialCount.wacc new file mode 100644 index 0000000..d07f600 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/sequentialCount.wacc @@ -0,0 +1,41 @@ +# simple sequential counting + +# Output: +# Can you count to 10? +# 1 +# 2 +# 3 +# 4 +# 5 +# 6 +# 7 +# 8 +# 9 +# 10 +# + +# Program: + +begin + int i = 1 ; + println "Can you count to 10?" ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i ; + i = i + 1 ; + println i +end diff --git a/src/test/wacc/waccPrograms/valid/expressions/stringEqualsExpr.wacc b/src/test/wacc/waccPrograms/valid/expressions/stringEqualsExpr.wacc new file mode 100644 index 0000000..0579b1b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/expressions/stringEqualsExpr.wacc @@ -0,0 +1,19 @@ +# evaluating string equality + +# Output: +# true +# false +# false +# + +# Program: + +begin + string s1 = "Hello" ; + string s2 = "foo" ; + string s3 = "bar" ; + bool b = s1 == s1 ; + println b ; + println s1 == s2 ; + println s2 == s3 +end diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/asciiTableImport.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/asciiTableImport.wacc new file mode 100644 index 0000000..d1fa625 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/asciiTableImport.wacc @@ -0,0 +1,117 @@ +# imports asciiTable.wacc and recreates its functionality + +# Output: +# Ascii character lookup table: +# ------------- +# | 32 = | +# | 33 = ! | +# | 34 = " | +# | 35 = # | +# | 36 = $ | +# | 37 = % | +# | 38 = & | +# | 39 = ' | +# | 40 = ( | +# | 41 = ) | +# | 42 = * | +# | 43 = + | +# | 44 = , | +# | 45 = - | +# | 46 = . | +# | 47 = / | +# | 48 = 0 | +# | 49 = 1 | +# | 50 = 2 | +# | 51 = 3 | +# | 52 = 4 | +# | 53 = 5 | +# | 54 = 6 | +# | 55 = 7 | +# | 56 = 8 | +# | 57 = 9 | +# | 58 = : | +# | 59 = ; | +# | 60 = < | +# | 61 = = | +# | 62 = > | +# | 63 = ? | +# | 64 = @ | +# | 65 = A | +# | 66 = B | +# | 67 = C | +# | 68 = D | +# | 69 = E | +# | 70 = F | +# | 71 = G | +# | 72 = H | +# | 73 = I | +# | 74 = J | +# | 75 = K | +# | 76 = L | +# | 77 = M | +# | 78 = N | +# | 79 = O | +# | 80 = P | +# | 81 = Q | +# | 82 = R | +# | 83 = S | +# | 84 = T | +# | 85 = U | +# | 86 = V | +# | 87 = W | +# | 88 = X | +# | 89 = Y | +# | 90 = Z | +# | 91 = [ | +# | 92 = \ | +# | 93 = ] | +# | 94 = ^ | +# | 95 = _ | +# | 96 = ` | +# | 97 = a | +# | 98 = b | +# | 99 = c | +# | 100 = d | +# | 101 = e | +# | 102 = f | +# | 103 = g | +# | 104 = h | +# | 105 = i | +# | 106 = j | +# | 107 = k | +# | 108 = l | +# | 109 = m | +# | 110 = n | +# | 111 = o | +# | 112 = p | +# | 113 = q | +# | 114 = r | +# | 115 = s | +# | 116 = t | +# | 117 = u | +# | 118 = v | +# | 119 = w | +# | 120 = x | +# | 121 = y | +# | 122 = z | +# | 123 = { | +# | 124 = | | +# | 125 = } | +# | 126 = ~ | +# ------------- +# + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/simple_functions/asciiTable.wacc" + +begin + println "Ascii character lookup table:" ; + bool r = call printLine(13) ; + int num = ord ' ' ; + while num < 127 do + r = call printMap(num) ; + num = num + 1 + done ; + r = call printLine(13) +end diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/basicImport.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/basicImport.wacc new file mode 100644 index 0000000..46198a4 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/basicImport.wacc @@ -0,0 +1,16 @@ +# imports the inc function from incFunction.wacc and calls it twice + +# Output: +# 2 +# + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/simple_functions/incFunction.wacc" + +begin + int x = 0 ; + x = call inc(x) ; + x = call inc(x) ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/manyImports.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/manyImports.wacc new file mode 100644 index 0000000..371cdf0 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/manyImports.wacc @@ -0,0 +1,20 @@ +# Uses functions imported from different files + +# Output: + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/simple_functions/incFunction.wacc" +import "src/test/wacc/waccPrograms/valid/function/simple_functions/functionArrayDoesntOverwrite.wacc" +import "src/test/wacc/waccPrograms/valid/function/simple_functions/functionMultiReturns.wacc" +import "src/test/wacc/waccPrograms/valid/function/simple_functions/functionManyArguments.wacc" +import "src/test/wacc/waccPrograms/valid/function/simple_functions/functionReturnPair.wacc" +import "src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciFullRec.wacc" +import "src/test/wacc/waccPrograms/valid/function/nested_functions/mutualRecursion.wacc" +import "src/test/wacc/waccPrograms/valid/function/nested_functions/simpleRecursion.wacc" +import "src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveImport.wacc" + +begin + skip +end + diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/multipleImports.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/multipleImports.wacc new file mode 100644 index 0000000..4965c76 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/multipleImports.wacc @@ -0,0 +1,19 @@ +# Uses functions imported from different files + +# Output: +# 9 +# 0 +# + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/simple_functions/incFunction.wacc" +import "src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueParameters.wacc" + +begin + int x = 8 ; + x = call inc(x) ; + println x; + int s = call f(x); + println s +end diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/nestedImport.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/nestedImport.wacc new file mode 100644 index 0000000..2c8c57e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/nestedImport.wacc @@ -0,0 +1,15 @@ +# attempts to use a function by importing a file that imports another file + +# Output: +# 1 +# + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/imported_functions/basicImport.wacc" + +begin + int x = 0 ; + x = call inc(x) ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/overloadedImport.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/overloadedImport.wacc new file mode 100644 index 0000000..780dc96 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/overloadedImport.wacc @@ -0,0 +1,18 @@ +# Overloading function f with different signatures in their parameeter + +# Output: +# 0 +# 97 +# + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueParameters.wacc" + +begin + int x = call f(1); + int y = call f('a'); + + println x; + println y +end diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveImport.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveImport.wacc new file mode 100644 index 0000000..654f803 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveImport.wacc @@ -0,0 +1,29 @@ +# Imports a recursive function + +# Input: 10 + +# Output: +# Please enter the size of the triangle to print: +# ---------- +# --------- +# -------- +# ------- +# ------ +# ----- +# ---- +# --- +# -- +# - +# + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/nested_functions/printInputTriangle.wacc" + +begin + println "Please enter the size of the triangle to print:" ; + int x = 0; + + read x; + int s = call f(x) +end diff --git a/src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveManyImports.wacc b/src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveManyImports.wacc new file mode 100644 index 0000000..ba71b40 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/imported_functions/recursiveManyImports.wacc @@ -0,0 +1,34 @@ +# Imports a recursive function + +# Input: 10 + +# Output: +# 9 +# Please enter the size of the triangle to print: +# ---------- +# --------- +# -------- +# ------- +# ------ +# ----- +# ---- +# --- +# -- +# - +# + +# Program: + +import "src/test/wacc/waccPrograms/valid/function/imported_functions/manyImports.wacc" + +begin + int s = 8 ; + s = call inc(s) ; + println s; + + println "Please enter the size of the triangle to print:" ; + int x = 0; + + read x; + int r = call f(x) +end diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciFullRec.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciFullRec.wacc new file mode 100644 index 0000000..900b6d5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciFullRec.wacc @@ -0,0 +1,35 @@ +# recursively calculate the nth fibonacci number + +# Input: 30 + +# Output: +# This program calculates the nth fibonacci number recursively. +# Please enter n (should not be too large): The input n is 30 +# The nth fibonacci number is 832040 +# + +# Program: + +begin + int fibonacci(int n) is + if n <= 1 + then + return n + else + skip + fi ; + int f1 = call fibonacci(n - 1) ; + int f2 = call fibonacci(n - 2) ; + return f1 + f2 + end + + println "This program calculates the nth fibonacci number recursively." ; + print "Please enter n (should not be too large): " ; + int n = 0; + read n ; + print "The input n is " ; + println n ; + print "The nth fibonacci number is " ; + int result = call fibonacci(n) ; + println result +end diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciRecursive.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciRecursive.wacc new file mode 100644 index 0000000..f955f6e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/fibonacciRecursive.wacc @@ -0,0 +1,35 @@ +# recursive calculation of the first 20 fibonacci numbers + +# Output: +# The first 20 fibonacci numbers are: +# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181... +# + +# Program: + +begin + int fibonacci(int n, bool toPrint) is + if n <= 1 + then + return n + else + skip + fi ; + int f1 = call fibonacci(n - 1, toPrint) ; + if toPrint + then + print f1 ; + print ", " + else + skip + fi ; + int f2 = call fibonacci(n - 2, false) ; + return f1 + f2 + end + + println "The first 20 fibonacci numbers are:" ; + print "0, " ; + int result = call fibonacci(19, true) ; + print result ; + println "..." +end diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/fixedPointRealArithmetic.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/fixedPointRealArithmetic.wacc new file mode 100644 index 0000000..9bdbad2 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/fixedPointRealArithmetic.wacc @@ -0,0 +1,123 @@ +# This program implements floating-point type using integers. +# The details about how it is done can found here: +# http://www.cse.iitd.ernet.in/~sbansal/csl373/pintos/doc/pintos_7.html#SEC135 +# +# Basically, our integer have 32 bits. We use the first bit for sign, the next +# 17 bits for value above the decimal digit and the last 14 bits for the values +# after the decimal digits. +# +# We call the number 17 above p, and the number 14 above q. +# We have f = 2**q. +# + +# Output: +# Using fixed-point real: 10 / 3 * 3 = 10 +# + +# Program: + +begin + # Returns the number of bits behind the decimal points. + int q() is + return 14 + end + + # Because we do not have bitwise shit in the language, we have to calculate it manually. + int power(int base, int amount) is + int result = 1 ; + while amount > 0 do + result = result * base ; + amount = amount - 1 + done ; + return result + end + + int f() is + int qq = call q() ; + # f = 2**q + int f = call power(2, qq) ; + return f + end + + # The implementation of the following functions are translated from the URI above. + # Arguments start with 'x' have type fixed-point. Those start with 'n' have type integer. + + int intToFixedPoint(int n) is + int ff = call f() ; + return n * ff + end + + int fixedPointToIntRoundDown(int x) is + int ff = call f() ; + return x / ff + end + + int fixedPointToIntRoundNear(int x) is + int ff = call f() ; + if x >= 0 + then + return (x + ff / 2) / ff + else + return (x - ff / 2) / ff + fi + end + + int add(int x1, int x2) is + return x1 + x2 + end + + int subtract(int x1, int x2) is + return x1 - x2 + end + + int addByInt(int x, int n) is + int ff = call f() ; + return x + n * ff + end + + int subtractByInt(int x, int n) is + int ff = call f() ; + return x - n * ff + end + + int multiply(int x1, int x2) is + # We don't have int_64 in our language so we just ignore the overflow + int ff = call f() ; + return x1 * x2 / ff + end + + int multiplyByInt(int x, int n) is + return x * n + end + + int divide(int x1, int x2) is + # We don't have int_64 in our language so we just ignore the overflow + int ff = call f() ; + return x1 * ff / x2 + end + + int divideByInt(int x, int n) is + return x / n + end + + # Main function + int n1 = 10 ; + int n2 = 3 ; + + print "Using fixed-point real: " ; + print n1 ; + print " / " ; + print n2 ; + print " * " ; + print n2 ; + print " = " ; + + int x = call intToFixedPoint(n1) ; + x = call divideByInt(x, n2) ; + x = call multiplyByInt(x, n2) ; + int result = call fixedPointToIntRoundNear(x) ; + println result +end + + + diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/functionConditionalReturn.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/functionConditionalReturn.wacc new file mode 100644 index 0000000..a113e91 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/functionConditionalReturn.wacc @@ -0,0 +1,19 @@ +# program has function which only contains an if statement and a return in each branch + +# Output: +# true +# + +# Program: + +begin + bool f() is + if true then + return true + else + return false + fi + end + bool x = call f(); + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/mutualRecursion.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/mutualRecursion.wacc new file mode 100644 index 0000000..a5e9b9b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/mutualRecursion.wacc @@ -0,0 +1,46 @@ +# a pair of mutually recursive functions + +# Output: +# r1: sending 8 +# r2: received 8 +# r1: sending 7 +# r2: received 7 +# r1: sending 6 +# r2: received 6 +# r1: sending 5 +# r2: received 5 +# r1: sending 4 +# r2: received 4 +# r1: sending 3 +# r2: received 3 +# r1: sending 2 +# r2: received 2 +# r1: sending 1 +# r2: received 1 +# + +# Program: + +begin + int r1(int x) is + if x == 0 + then + skip + else + print "r1: sending " ; + println x ; + int y = call r2(x) + fi ; + return 42 + end + + int r2(int y) is + print "r2: received " ; + println y ; + int z = call r1(y - 1) ; + return 44 + end + + int x = 0 ; + x = call r1(8) +end diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/printInputTriangle.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/printInputTriangle.wacc new file mode 100644 index 0000000..371d3dd --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/printInputTriangle.wacc @@ -0,0 +1,45 @@ +# print a user-specified sized triangle + +# Input: 13 + +# Output: +# Please enter the size of the triangle to print: +# ------------- +# ------------ +# ----------- +# ---------- +# --------- +# -------- +# ------- +# ------ +# ----- +# ---- +# --- +# -- +# - +# + +# Program: + +begin + int f(int x) is + if x == 0 then + skip + else + int i = x ; + while i > 0 do + print "-" ; + i = i - 1 + done ; + println "" ; + int s = call f(x - 1) + fi ; + return 0 + end + + println "Please enter the size of the triangle to print:" ; + int x = 0; + + read x; + int s = call f(x) +end diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/printTriangle.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/printTriangle.wacc new file mode 100644 index 0000000..bf2cbc1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/printTriangle.wacc @@ -0,0 +1,33 @@ +# print a fixed size triangle + +# Output: +# -------- +# ------- +# ------ +# ----- +# ---- +# --- +# -- +# - +# + +# Program: + +begin + int f(int x) is + if x == 0 then + skip + else + int i = x ; + while i > 0 do + print "-" ; + i = i - 1 + done ; + println "" ; + int s = call f(x - 1) + fi ; + return 0 + end + + int s = call f(8) +end diff --git a/src/test/wacc/waccPrograms/valid/function/nested_functions/simpleRecursion.wacc b/src/test/wacc/waccPrograms/valid/function/nested_functions/simpleRecursion.wacc new file mode 100644 index 0000000..d36acd0 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/nested_functions/simpleRecursion.wacc @@ -0,0 +1,20 @@ +# a simple recursive function + +# Output: + +# Program: + +begin + int rec(int x) is + if x == 0 + then + skip + else + int y = call rec(x - 1) + fi ; + return 42 + end + + int x = 0 ; + x = call rec(8) +end diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/complexOverload.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/complexOverload.wacc new file mode 100644 index 0000000..a8922da --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/complexOverload.wacc @@ -0,0 +1,116 @@ +# A more involved valid WACC program demonstrating a variety of overloads: +# - Overloads "process" for int, char, and int[]. +# - Also overloads "process" for 2 vs 3 ints. +# - Uses pairs inside an overload. +# - Reads input from user and calls relevant overload. +# - Prints combined results. + +# Input: 10 a +# + +# Output: +# Enter an integer: +# Enter a single character: +# Processing integer: 10 -> 11 +# Processing character: a -> 97 +# Processing array -> sum: 46 +# Processing triple sum -> 16 +# Pair processed: FST= 5, SND= 15 +# Combined result: 206 +# + +# Program: + +begin + + # Overload 1: process(int x) + int process(int x) is + # do something trivial: add 1 + print "Processing integer: " ; + print x ; + print " -> " ; + println (x + 1) ; + return x + 1 + end + + # Overload 2: process(char c) + int process(char c) is + # convert to ASCII code + print "Processing character: " ; + print c ; + print " -> " ; + int code = ord c ; + println code ; + return code + end + + # Overload 3: process(int[] arr) + int process(int[] arr) is + # sum elements of the array + int s = 0 ; + int i = 0 ; + while i < len arr do + s = s + arr[i] ; + i = i + 1 + done ; + print "Processing array -> sum: " ; + println s ; + return s + end + + # Overload 4: process(int x, int y) + int process(int x, int y) is + # sum + int total = x + y ; + return total + end + + # Overload 5: process(int x, int y, int z) + int process(int x, int y, int z) is + int sum = x + y + z ; + print "Processing triple sum -> " ; + println sum ; + return sum + end + + # Overload 6: process(pair(int,int) p) + int process(pair(int,int) p) is + int first = fst p ; + int second = snd p ; + print "Pair processed: FST= " ; + print first ; + print ", SND= " ; + println second ; + return first + second + end + + # Make an int array + int[] arr = [10, 20, 16] ; + + # Make a pair + pair(int,int) p = newpair(5, 15) ; + + # Try out calls: + # 1) ask user for an int, then a char + println "Enter an integer:" ; + int userInt = 0 ; + read userInt ; + skip ; + + println "Enter a single character:" ; + char userChar = '\0' ; + read userChar ; + skip ; + + int r1 = call process(userInt) ; # calls process(int) + int r2 = call process(userChar) ; # calls process(char) + int r3 = call process(arr) ; # calls process(int[]) + int r4 = call process(5, 11) ; # calls process(int,int) + int r5 = call process(1, 5, 10) ; # calls process(int,int,int) + int r6 = call process(p) ; # calls process(pair(int,int)) + + # Print final combined result + print "Combined result: " ; + println (r1 + r2 + r3 + r4 + r5 + r6) + +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/differentParameterCounts.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/differentParameterCounts.wacc new file mode 100644 index 0000000..ae47136 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/differentParameterCounts.wacc @@ -0,0 +1,24 @@ +# Overloads "sum" based on different numbers of parameters. + +# Output: +# 6 +# 10 +# + +# Program: + +begin + int sum(int x, int y) is + return x + y + end + + int sum(int x, int y, int z) is + return x + y + z + end + + int r1 = call sum(2, 4) ; # resolves to sum(int, int) + int r2 = call sum(3, 4, 3) ; # resolves to sum(int, int, int) + + println r1 ; + println r2 +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString.wacc new file mode 100644 index 0000000..3c1f77f --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString.wacc @@ -0,0 +1,24 @@ +# Overloads "fun" in a way that calling with a char array + +# Output: +# hello world +# true +# + +# Program: + +begin + string fun(string x) is + return x + end + + bool fun(char x) is + return x == 'h' + end + + char[] text = ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] ; + string result = call fun(text) ; + bool exclaim = call fun(text[0]) ; + println result ; + println exclaim +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString2.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString2.wacc new file mode 100644 index 0000000..fa9953b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/functionCharIntoString2.wacc @@ -0,0 +1,24 @@ +# Overloads "fun" in a way that calling with a char array + +# Output: +# hello world +# h +# + +# Program: + +begin + string fun(string x) is + return x + end + + char fun(char x) is + return x + end + + char[] text = ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] ; + string result = call fun(text) ; + char exclaim = call fun(text[0]) ; + println result ; + println exclaim +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithArrays.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithArrays.wacc new file mode 100644 index 0000000..1982562 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithArrays.wacc @@ -0,0 +1,26 @@ +# Tests overloading functions with array parameters vs. int parameters. + +# Output: +# 3 +# 12 +# + +# Program: + +begin + int size(int[] arr) is + return len arr + end + + int size(int x) is + # Return the integer itself (pretend "size" means "value" here) + return x + end + + int[] arr = [4, 5, 6] ; + int arraySize = call size(arr) ; + int justValue = call size(12) ; + + println arraySize ; + println justValue +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithPairs.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithPairs.wacc new file mode 100644 index 0000000..2a3ebb4 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/overloadingWithPairs.wacc @@ -0,0 +1,30 @@ +# Overloads "getFirst" with a pair(int, int) vs. pair(char, bool). +# Illustrates pair usage in overload resolution. + +# Output: +# 10 +# a +# + +# Program: + +begin + int getFirst(pair(int,int) p) is + int x = fst p ; + return x + end + + char getFirst(pair(char,bool) p) is + char x = fst p ; + return x + end + + pair(int,int) p1 = newpair(10, 20) ; + pair(char,bool) p2 = newpair('a', true) ; + + int r1 = call getFirst(p1) ; + char r2 = call getFirst(p2) ; + + println r1 ; + println r2 +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueParameters.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueParameters.wacc new file mode 100644 index 0000000..0e3fc72 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueParameters.wacc @@ -0,0 +1,24 @@ +# Overloading function f with different signatures in their parameeter + +# Output: +# 0 +# 97 +# + +# Program: + +begin + int f(int x) is + return 0 + end + + int f(char x) is + return ord x + end + + int x = call f(1); + int y = call f('a'); + + println x; + println y +end diff --git a/src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueSignatures.wacc b/src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueSignatures.wacc new file mode 100644 index 0000000..9798ea9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/overload_functions/uniqueSignatures.wacc @@ -0,0 +1,24 @@ +# Overloading function f with completely unique signatures + +# Output: +# 1 +# a +# + +# Program: + +begin + int f(int x) is + return x + end + + char f(char x) is + return x + end + + int x = call f(1); + char y = call f('a'); + + println x; + println y +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/argScopeCanBeShadowed.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/argScopeCanBeShadowed.wacc new file mode 100644 index 0000000..6872ee1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/argScopeCanBeShadowed.wacc @@ -0,0 +1,18 @@ +# Arguments can be shadowed by the function body + +# Output: +# true +# + +#Program: + +begin + bool f(int x) is + bool x = true ; + return x + end + + bool x = call f(5); + println x + +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/asciiTable.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/asciiTable.wacc new file mode 100644 index 0000000..979e58b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/asciiTable.wacc @@ -0,0 +1,139 @@ +# print out a lookup table for Ascii character representations + +# Output: +# Ascii character lookup table: +# ------------- +# | 32 = | +# | 33 = ! | +# | 34 = " | +# | 35 = # | +# | 36 = $ | +# | 37 = % | +# | 38 = & | +# | 39 = ' | +# | 40 = ( | +# | 41 = ) | +# | 42 = * | +# | 43 = + | +# | 44 = , | +# | 45 = - | +# | 46 = . | +# | 47 = / | +# | 48 = 0 | +# | 49 = 1 | +# | 50 = 2 | +# | 51 = 3 | +# | 52 = 4 | +# | 53 = 5 | +# | 54 = 6 | +# | 55 = 7 | +# | 56 = 8 | +# | 57 = 9 | +# | 58 = : | +# | 59 = ; | +# | 60 = < | +# | 61 = = | +# | 62 = > | +# | 63 = ? | +# | 64 = @ | +# | 65 = A | +# | 66 = B | +# | 67 = C | +# | 68 = D | +# | 69 = E | +# | 70 = F | +# | 71 = G | +# | 72 = H | +# | 73 = I | +# | 74 = J | +# | 75 = K | +# | 76 = L | +# | 77 = M | +# | 78 = N | +# | 79 = O | +# | 80 = P | +# | 81 = Q | +# | 82 = R | +# | 83 = S | +# | 84 = T | +# | 85 = U | +# | 86 = V | +# | 87 = W | +# | 88 = X | +# | 89 = Y | +# | 90 = Z | +# | 91 = [ | +# | 92 = \ | +# | 93 = ] | +# | 94 = ^ | +# | 95 = _ | +# | 96 = ` | +# | 97 = a | +# | 98 = b | +# | 99 = c | +# | 100 = d | +# | 101 = e | +# | 102 = f | +# | 103 = g | +# | 104 = h | +# | 105 = i | +# | 106 = j | +# | 107 = k | +# | 108 = l | +# | 109 = m | +# | 110 = n | +# | 111 = o | +# | 112 = p | +# | 113 = q | +# | 114 = r | +# | 115 = s | +# | 116 = t | +# | 117 = u | +# | 118 = v | +# | 119 = w | +# | 120 = x | +# | 121 = y | +# | 122 = z | +# | 123 = { | +# | 124 = | | +# | 125 = } | +# | 126 = ~ | +# ------------- +# + +# Program: + +begin + bool printLine(int n) is + int i = 0 ; + while i < n do + print "-" ; + i = i + 1 + done ; + println "" ; + return true + end + + bool printMap(int n) is + print "| " ; + if n <100 then + print " " + else + skip + fi ; + print n ; + print " = " ; + print chr n ; + println " |" ; + return true + end + + println "Ascii character lookup table:" ; + bool r = call printLine(13) ; + int num = ord ' ' ; + while num < 127 do + r = call printMap(num) ; + num = num + 1 + done ; + r = call printLine(13) +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/clashNames.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/clashNames.wacc new file mode 100644 index 0000000..f613ccb --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/clashNames.wacc @@ -0,0 +1,21 @@ +# we should be able to use any function names, even if they clash with libc + +# Output: + +# Exit: +# 101 + +# Program: + +begin + int malloc() is return 42 end + int scanf() is return 37 end + int printf() is return 20 end + int puts() is return 2 end + + int u = call malloc(); + int x = call scanf(); + int y = call printf(); + int z = call puts(); + exit (u + x + y + z) +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionArrayDoesntOverwrite.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionArrayDoesntOverwrite.wacc new file mode 100644 index 0000000..cd73b42 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionArrayDoesntOverwrite.wacc @@ -0,0 +1,18 @@ +# + +# Output: +# true +# + +# Program: + +begin + bool fun(int x) is + int[] y = [x, x, x] ; + return x == 2 + end + + int[] text = [2, 2, 2] ; + bool isMatch = call fun(2) ; + println isMatch +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionDeclaration.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionDeclaration.wacc new file mode 100644 index 0000000..87ab6b9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionDeclaration.wacc @@ -0,0 +1,12 @@ +# a simple function is declared, but not called + +# Output: + +# Program: + +begin + int f() is + return 0 + end + skip +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionDoubleReturn.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionDoubleReturn.wacc new file mode 100644 index 0000000..ba77ebd --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionDoubleReturn.wacc @@ -0,0 +1,16 @@ +# a simple function with two back-to-back returns at the end. + +# Output: +# 3 +# + +# Program: + +begin + int f() is + return 3; + return 5 + end + int ret = call f(); + println ret +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionIfReturns.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionIfReturns.wacc new file mode 100644 index 0000000..ff7aab2 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionIfReturns.wacc @@ -0,0 +1,23 @@ +# a simple function with nested returns inside an if-statement after a return + +# Output: +# go +# 1 +# + +begin + int f() is + println "go"; + return 1; + if true then + println "a"; + return 2 + else + println "b"; + return 3 + fi + end + + int ret = call f(); + println ret +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionManyArguments.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionManyArguments.wacc new file mode 100644 index 0000000..ff98b4d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionManyArguments.wacc @@ -0,0 +1,36 @@ +# a function with varied inputs + +# Output: +# a is 42 +# b is true +# c is u +# d is hello +# e is #addrs# +# f is #addrs# +# answer is g +# + +# Program: + +begin + char doSomething(int a, bool b, char c, string d, bool[] e, int[] f) is + print "a is " ; + println a ; + print "b is " ; + println b ; + print "c is " ; + println c ; + print "d is " ; + println d ; + print "e is " ; + println e ; + print "f is " ; + println f ; + return 'g' + end + bool[] bools = [ false, true ] ; + int[] ints = [ 1, 2 ] ; + char answer = call doSomething(42, true, 'u', "hello", bools, ints) ; + print "answer is " ; + println answer +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionMultiReturns.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionMultiReturns.wacc new file mode 100644 index 0000000..101530b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionMultiReturns.wacc @@ -0,0 +1,20 @@ +# a simple function with multiple returns, importantly one at the end. + +# Output: +# 1 +# + +# Program: + +begin + int returnInWhile() is + while true do + return 1 ; + println "How on Earth did we get here?" + done ; + return 2 + end + + int x = call returnInWhile() ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments.wacc new file mode 100644 index 0000000..c037a44 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments.wacc @@ -0,0 +1,22 @@ +# Functions should be able to handle when they have more than 8 arguments + +# Output: +# 220 +# + + +# Program: + +begin + int f (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) is + i = 9; + j = 10; + k = 11; + int result = k * j + k + a; + + return result + end + + int r = call f(99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99); + println r +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments2.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments2.wacc new file mode 100644 index 0000000..f41025d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionOverArguments2.wacc @@ -0,0 +1,63 @@ +# Functions should be able to handle when they have more than 8 arguments and calling within themselves +# They should also be able to save those values to the stack along with registers that stored parameters + +# Output: +# 0 +# 1 +# 2 +# 3 +# 4 +# 5 +# 6 +# 7 +# 8 +# 9 +# 10 +# 55 +# 10 +# 10 +# 10 +# 10 +# 10 +# 5 +# 6 +# 7 +# 8 +# 9 +# 10 +# 95 +# + + +# Program: + +begin + int f (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) is + int sum = call g(a, b, c, d, e, f, g, h, i, j, k); + println sum; + + int result = call g(a + k, b + j, c + i, d + h, e + g, f, g, h, i, j, k); + + return result + end + + int g (int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k) is + println a; + println b; + println c; + println d; + println e; + println f; + println g; + println h; + println i; + println j; + println k; + int result = a + b + c + d + e + f + g + h + i + j + k; + + return result + end + + int r = call f(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + println r +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionReturnPair.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionReturnPair.wacc new file mode 100644 index 0000000..4799e1d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionReturnPair.wacc @@ -0,0 +1,19 @@ +# creates a pair which is returned from a function + +# Output: +# 10 +# + +# Program: + +begin + + pair(int, int) getPair() is + pair(int, int) p = newpair(10,15); + return p + end + + pair(int, int) p = call getPair(); + int x = fst p; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimple.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimple.wacc new file mode 100644 index 0000000..3553bf5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimple.wacc @@ -0,0 +1,15 @@ +# a simple function definition and call + +# Output: +# 0 +# + +# Program: + +begin + int f() is + return 0 + end + int x = call f() ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimpleLoop.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimpleLoop.wacc new file mode 100644 index 0000000..3c32028 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionSimpleLoop.wacc @@ -0,0 +1,19 @@ +# define and call a function with a simple loop + +# Output: +# 10 +# + +# Program: + +begin + int f(int n) is + int i = 0; + while i < n do + i = i + 1 + done; + return i + end + int x = call f(10) ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/functionUpdateParameter.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionUpdateParameter.wacc new file mode 100644 index 0000000..6cdd405 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/functionUpdateParameter.wacc @@ -0,0 +1,30 @@ +# test that the passed parameter can be updated and used +# and that y remains the same + +# Output: +# y is 1 +# x is 1 +# x is now 5 +# y is still 1 +# + +# Program: + +begin + + int f(int x) is + print "x is "; + println x; + x = 5; + print "x is now "; + println x; + return x + end + + int y = 1; + print "y is "; + println y; + int x = call f(y); + print "y is still "; + println y +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/incFunction.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/incFunction.wacc new file mode 100644 index 0000000..f428f43 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/incFunction.wacc @@ -0,0 +1,21 @@ +# a simple increment function definition and usage + +# Output: +# 1 +# 4 +# + +# Program: + +begin + int inc(int x) is + return x + 1 + end + int x = 0 ; + x = call inc(x) ; + println x ; + x = call inc(x) ; + x = call inc(x) ; + x = call inc(x) ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/lotsOfLocals.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/lotsOfLocals.wacc new file mode 100644 index 0000000..c4df838 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/lotsOfLocals.wacc @@ -0,0 +1,32 @@ +# Functions with lots of local arguments should work ok +# Thanks to Kristina Zimina for finding this + +# Output: +# 5 +# 8 +# + +# Program: + +begin + int f (int a, int b, int c, int d, int e, int k) is + int x1 = 1; + int x2 = 2; + int x3 = 3; + int x4 = 4; + char x5 = '5'; + char x6 = '6'; + char x7 = '7'; + int x8 = 8; + begin + char y1 = 'a'; + char y2 = 'b'; + int y = 5 ; + println y + end ; + return x8 + end + + int r = call f(1, 2, 3, 4, 5, 6); + println r +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsChar.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsChar.wacc new file mode 100644 index 0000000..5af79a3 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsChar.wacc @@ -0,0 +1,27 @@ +# tests a function with more arguments than fit into registers + +# Output: +# A +# b +# + +# Program: + +begin + # w, x, y and z are stack allocated on aarch32 + # y and z are stack allocated on x86-64 + char f(int s, int t, int u, int v, int w, int x, char y, bool z) is + int i = u + v ; + int j = w + x ; + if z + then return chr (ord y - i * j) + else return y + fi + end + + char r1 = call f(0, 0, 3, 5, 1, 3, 'a', true) ; + println r1 ; + + char r2 = call f(0, 0, 3, 5, 1, 3, 'b', false) ; + println r2 +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsInt.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsInt.wacc new file mode 100644 index 0000000..9e2cfd3 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/manyArgumentsInt.wacc @@ -0,0 +1,21 @@ +# tests a function with more arguments than fit into registers + +# Output: +# 23 +# + +# Program: + +begin + # w, x, y and z are stack allocated on aarch32 + # y and z are stack allocated on x86-64 + int f(int s, int t, int u, int v, int w, int x, int y, int z) is + int i = u + v ; + int j = w * x ; + int k = y - z ; + return i + j * k + end + + int r = call f(0, 0, 1, 4, 2, 3, 7, 4) ; + println r +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/negFunction.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/negFunction.wacc new file mode 100644 index 0000000..972f1ac --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/negFunction.wacc @@ -0,0 +1,23 @@ +# a simple negation function definition and usage + +# Output: +# true +# false +# true +# + +# Program: + +begin + bool neg(bool b) is + return !b + end + bool b = true ; + println b ; + b = call neg(b) ; + println b ; + b = call neg(b) ; + b = call neg(b) ; + b = call neg(b) ; + println b +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/punning.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/punning.wacc new file mode 100644 index 0000000..a56b6af --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/punning.wacc @@ -0,0 +1,15 @@ +# Functions should be able to have the same name as variables +# Thanks to Jordan Hall + +# Output: +# 0 +# + +begin + int inc(int x) is + return x + 1 + end + int inc = 0; + int tmp = call inc(inc); + println inc +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName.wacc new file mode 100644 index 0000000..cb606c5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName.wacc @@ -0,0 +1,16 @@ +# program with function that has same parameter name as function + +# Output: +# 99 +# + +# Program: + +begin + int f(int f) is + return f + end + + int x = call f(99); + println x +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName2.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName2.wacc new file mode 100644 index 0000000..2a81747 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/sameArgName2.wacc @@ -0,0 +1,17 @@ +# program with function that has same parameter name as function +# and also returns to same named variable + +# Output: +# 99 +# + +# Program: + +begin + int f(int f) is + return f + end + + int f = call f(99); + println f +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/sameNameAsVar.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/sameNameAsVar.wacc new file mode 100644 index 0000000..6edfeca --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/sameNameAsVar.wacc @@ -0,0 +1,16 @@ +# program with function that has same name as a variable + +# Output: +# 5 +# + +# Program: + +begin + int f() is + return 5 + end + + int f = call f(); + println f +end diff --git a/src/test/wacc/waccPrograms/valid/function/simple_functions/usesArgumentWhilstMakingArgument.wacc b/src/test/wacc/waccPrograms/valid/function/simple_functions/usesArgumentWhilstMakingArgument.wacc new file mode 100644 index 0000000..7f11db5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/function/simple_functions/usesArgumentWhilstMakingArgument.wacc @@ -0,0 +1,25 @@ +# tests a function with more arguments than fit into registers + +# Output: +# 12 +# -4 +# 32 +# + +# Program: + +begin + int f(int u, int v) is + int x = call g(u + v, u - v, u * v) ; + return x + end + + int g(int x, int y, int z) is + println(x) ; + println(y) ; + println(z) ; + return 0 + end + + int r = call f(4, 8) +end diff --git a/src/test/wacc/waccPrograms/valid/if/if1.wacc b/src/test/wacc/waccPrograms/valid/if/if1.wacc new file mode 100644 index 0000000..2e73ec7 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/if1.wacc @@ -0,0 +1,17 @@ +# Simple conditional statement with int comparison test + +# Output: +# correct +# + +# Program: + +begin + int a = 13; + if a == 13 + then + println "correct" + else + println "incorrect" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/if2.wacc b/src/test/wacc/waccPrograms/valid/if/if2.wacc new file mode 100644 index 0000000..01e767d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/if2.wacc @@ -0,0 +1,17 @@ +# Simple conditional statement with int comparison test + +# Output: +# correct +# + +# Program: + +begin + int a = 13; + if a != 13 + then + println "incorrect" + else + println "correct" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/if3.wacc b/src/test/wacc/waccPrograms/valid/if/if3.wacc new file mode 100644 index 0000000..24c5068 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/if3.wacc @@ -0,0 +1,18 @@ +# Simple conditional statement with int comparison test + +# Output: +# correct +# + +# Program: + +begin + int a = 13; + int b = 37; + if a < b + then + println "correct" + else + println "incorrect" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/if4.wacc b/src/test/wacc/waccPrograms/valid/if/if4.wacc new file mode 100644 index 0000000..171321d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/if4.wacc @@ -0,0 +1,18 @@ +# Simple conditional statement with boolen expression test + +# Output: +# correct +# + +# Program: + +begin + bool b = true; + bool c = false; + if b && c + then + println "incorrect" + else + println "correct" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/if5.wacc b/src/test/wacc/waccPrograms/valid/if/if5.wacc new file mode 100644 index 0000000..e7022b5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/if5.wacc @@ -0,0 +1,18 @@ +# Simple conditional statement with boolen expression test + +# Output: +# correct +# + +# Program: + +begin + bool b = true; + bool c = false; + if b || c + then + println "correct" + else + println "incorrect" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/if6.wacc b/src/test/wacc/waccPrograms/valid/if/if6.wacc new file mode 100644 index 0000000..092e044 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/if6.wacc @@ -0,0 +1,18 @@ +# Simple conditional statement with character comparison test + +# Output: +# correct +# + +# Program: + +begin + char c1 = 'f'; + char c2 = 'F'; + if c1 == c2 + then + println "incorrect" + else + println "correct" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/ifBasic.wacc b/src/test/wacc/waccPrograms/valid/if/ifBasic.wacc new file mode 100644 index 0000000..dc6b1d7 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/ifBasic.wacc @@ -0,0 +1,15 @@ +# simple if statement + +# Output: + +# Program: + +begin + if true + then + skip + else + skip + fi +end + diff --git a/src/test/wacc/waccPrograms/valid/if/ifFalse.wacc b/src/test/wacc/waccPrograms/valid/if/ifFalse.wacc new file mode 100644 index 0000000..aa93ded --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/ifFalse.wacc @@ -0,0 +1,16 @@ +# simple false if statement + +# Output: +# here +# + +# Program: + +begin + if false + then + println "not here" + else + println "here" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/ifTrue.wacc b/src/test/wacc/waccPrograms/valid/if/ifTrue.wacc new file mode 100644 index 0000000..3c38bc7 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/ifTrue.wacc @@ -0,0 +1,16 @@ +# simple true if statement + +# Output: +# here +# + +# Program: + +begin + if true + then + println "here" + else + println "not here" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/if/whitespace.wacc b/src/test/wacc/waccPrograms/valid/if/whitespace.wacc new file mode 100644 index 0000000..5e4cd8b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/if/whitespace.wacc @@ -0,0 +1,13 @@ +# Whitespace only important between keyword and variable tokens + +# Output: +# 1 +# + +# Program: + +begin + int a=13; + if a==13then a=1else a=0fi; + println a +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/checkRefPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/checkRefPair.wacc new file mode 100644 index 0000000..dd77516 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/checkRefPair.wacc @@ -0,0 +1,33 @@ +# create a pair(int, char) with a second reference to it and check it works + +# Output: +# #addrs# +# #addrs# +# true +# 10 +# 10 +# true +# a +# a +# true +# + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') ; + pair(int, char) q = p; + println p ; + println q ; + println p == q ; + int x = fst p ; + int y = fst q ; + println x ; + println y ; + println x == y ; + char c1 = snd p ; + char c2 = snd q ; + println c1 ; + println c2 ; + println c1 == c2 +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/createPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/createPair.wacc new file mode 100644 index 0000000..1129b08 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/createPair.wacc @@ -0,0 +1,9 @@ +# create a pair (int, int) + +# Output: + +# Program: + +begin + pair(int, int) p = newpair(10, 3) +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/createPair02.wacc b/src/test/wacc/waccPrograms/valid/pairs/createPair02.wacc new file mode 100644 index 0000000..a7ce60d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/createPair02.wacc @@ -0,0 +1,9 @@ +# create a pair (char, char) + +# Output: + +# Program: + +begin + pair(char, char) p = newpair('a', 'b') +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/createPair03.wacc b/src/test/wacc/waccPrograms/valid/pairs/createPair03.wacc new file mode 100644 index 0000000..d45340c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/createPair03.wacc @@ -0,0 +1,9 @@ +# create a pair (int, char) + +# Output: + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/createRefPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/createRefPair.wacc new file mode 100644 index 0000000..02b9870 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/createRefPair.wacc @@ -0,0 +1,10 @@ +# create a pair(int, char) with a second reference to it + +# Output: + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') ; + pair(int, char) q = p +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/freePairs.wacc b/src/test/wacc/waccPrograms/valid/pairs/freePairs.wacc new file mode 100644 index 0000000..d867321 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/freePairs.wacc @@ -0,0 +1,11 @@ +# Create and free a simple pair + +# Output: + +# Program: + +begin + pair(int, char) a = newpair(10, 'a') ; + free a +end + diff --git a/src/test/wacc/waccPrograms/valid/pairs/linkedList.wacc b/src/test/wacc/waccPrograms/valid/pairs/linkedList.wacc new file mode 100644 index 0000000..e930c2e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/linkedList.wacc @@ -0,0 +1,28 @@ +# create and print a linked list using pairs + +# Output: +# list = {1, 2, 4, 11} +# + +# Program: + +begin + pair(int, pair) p = newpair(11, null) ; + pair(int, pair) q = newpair(4, p) ; + pair(int, pair) r = newpair(2, q) ; + pair(int, pair) s = newpair(1, r) ; + print "list = {" ; + pair(int, pair) x = s ; + pair(int, pair) y = snd x ; + int f = 0; + while y != null do + f = fst x ; + print f ; + print ", " ; + x = y ; + y = snd x + done ; + f = fst x ; + print f ; + println "}" +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/nestedPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/nestedPair.wacc new file mode 100644 index 0000000..00f02e9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/nestedPair.wacc @@ -0,0 +1,10 @@ +# create a pair (int, pair (int, int) ) + +# Output: + +# Program: + +begin + pair(int, int) p = newpair(2, 3) ; + pair(int, pair) q = newpair(1, p) +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/nestedPairLeftAssign.wacc b/src/test/wacc/waccPrograms/valid/pairs/nestedPairLeftAssign.wacc new file mode 100644 index 0000000..dcb8f8a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/nestedPairLeftAssign.wacc @@ -0,0 +1,15 @@ +# nested pair assignments are legal as long as the right hand-side type is known + +# Output: +# 7 +# + +# Program: + +begin + pair(int, int) p = newpair(2, 3) ; + pair(int, pair) q = newpair(1, p) ; + fst snd q = 7 ; + int x = fst p ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/nestedPairRightExtract.wacc b/src/test/wacc/waccPrograms/valid/pairs/nestedPairRightExtract.wacc new file mode 100644 index 0000000..7adcd50 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/nestedPairRightExtract.wacc @@ -0,0 +1,14 @@ +# nested pair extractions are legal as long as the left hand-side type is known + +# Output: +# 2 +# + +# Program: + +begin + pair(int, int) p = newpair(2, 3) ; + pair(int, pair) q = newpair(1, p) ; + int x = fst snd q ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/null.wacc b/src/test/wacc/waccPrograms/valid/pairs/null.wacc new file mode 100644 index 0000000..75aee65 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/null.wacc @@ -0,0 +1,15 @@ +# null pair assignment + +# Output: +# (nil) +# (nil) +# + +# Program: + +begin + pair(pair, pair) p = null ; + println p ; + p = null ; + println p +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/pairExchangeArrayOk.wacc b/src/test/wacc/waccPrograms/valid/pairs/pairExchangeArrayOk.wacc new file mode 100644 index 0000000..4205824 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/pairExchangeArrayOk.wacc @@ -0,0 +1,12 @@ +# Assignment is legal when assigning array (even of unknown type) in nested pair extraction +# Thanks to Kristina Zimina for catching this + +# Output: + +# Program: + +begin + pair(int, int) p = newpair(4, 5); + pair(pair, int) q = newpair(p, 6); + fst fst q = [] +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/pairarray.wacc b/src/test/wacc/waccPrograms/valid/pairs/pairarray.wacc new file mode 100644 index 0000000..f2dcfb9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/pairarray.wacc @@ -0,0 +1,15 @@ +# ensures that pairs can be unpacked directly from arrays + +# Output: +# 3 +# + +# Program: + +begin + pair(int, int) p = newpair(5, 6); + pair(int, int)[] a = [p, p]; + fst a[0] = 3 ; + int x = fst a[1] ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/printNull.wacc b/src/test/wacc/waccPrograms/valid/pairs/printNull.wacc new file mode 100644 index 0000000..11c4081 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/printNull.wacc @@ -0,0 +1,11 @@ +# print the null reference + +# Output: +# (nil) +# + +# Program: + +begin + println null +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/printNullPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/printNullPair.wacc new file mode 100644 index 0000000..8df9bfb --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/printNullPair.wacc @@ -0,0 +1,12 @@ +# print pair a null pair + +# Output: +# (nil) +# + +# Program: + +begin + pair(pair, pair) p = null ; + println p +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/printPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/printPair.wacc new file mode 100644 index 0000000..d548b68 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/printPair.wacc @@ -0,0 +1,19 @@ +# print pair program + +# Output: +# #addrs# = (10, a) +# + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') ; + print p ; + print " = (" ; + int x = fst p ; + print x ; + print ", " ; + char c = snd p ; + print c ; + println ')' +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/printPairOfNulls.wacc b/src/test/wacc/waccPrograms/valid/pairs/printPairOfNulls.wacc new file mode 100644 index 0000000..aecc156 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/printPairOfNulls.wacc @@ -0,0 +1,19 @@ +# print a pair of null pairs + +# Output: +# #addrs# = ((nil),(nil)) +# + +# Program: + +begin + pair(pair, pair) p = newpair(null, null) ; + print p ; + print " = (" ; + pair(pair, pair) q = fst p ; + print q ; + print "," ; + pair(int, bool) r = snd p ; + print r ; + println ")" +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/readIntoPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/readIntoPair.wacc new file mode 100644 index 0000000..700dff5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/readIntoPair.wacc @@ -0,0 +1,21 @@ +# Read into a pair. + +# Input: 10 + +# Output: +# 1 +# 10 +# + +# Program: + +begin + pair(int, int) p = newpair(1, 2); + + int num = fst p; + println num; + + read fst p; + num = fst p; + println num +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/pairs/readPair.wacc b/src/test/wacc/waccPrograms/valid/pairs/readPair.wacc new file mode 100644 index 0000000..d132173 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/readPair.wacc @@ -0,0 +1,31 @@ +# construct a pair from supplied user input + +# Input: f 16 + +# Output: +# Please enter the first element (char): Please enter the second element (int): The first element was f +# The second element was 16 +# + +# Program: + +begin + pair(char, int) p = newpair('\0', 0) ; + print "Please enter the first element (char): " ; + char c = '0'; + read c ; + fst p = c ; + print "Please enter the second element (int): " ; + int i = 0 ; + read i ; + snd p = i ; + # Clear the value for c and i + c = '\0' ; + i = -1 ; + print "The first element was " ; + c = fst p ; + println c ; + print "The second element was " ; + i = snd p ; + println i +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/writeFst.wacc b/src/test/wacc/waccPrograms/valid/pairs/writeFst.wacc new file mode 100644 index 0000000..b0a7992 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/writeFst.wacc @@ -0,0 +1,17 @@ +# create a pair and write to its first element + +# Output: +# 10 +# 42 +# + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') ; + int f = fst p ; + println f ; + fst p = 42 ; + f = fst p ; + println f +end diff --git a/src/test/wacc/waccPrograms/valid/pairs/writeSnd.wacc b/src/test/wacc/waccPrograms/valid/pairs/writeSnd.wacc new file mode 100644 index 0000000..34f838f --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/pairs/writeSnd.wacc @@ -0,0 +1,17 @@ +# create a pair and write to its second element + +# Output: +# a +# Z +# + +# Program: + +begin + pair(int, char) p = newpair(10, 'a') ; + char s = snd p ; + println s ; + snd p = 'Z' ; + s = snd p ; + println s +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayNegBounds.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayNegBounds.wacc new file mode 100644 index 0000000..5829da2 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayNegBounds.wacc @@ -0,0 +1,15 @@ +# attempt out of bounds array access (this ought to seg fault or similar) + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int[] a = [43, 2, 18, 1] ; + int[] b = [1, 2, 3] ; + println a[-2] +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBounds.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBounds.wacc new file mode 100644 index 0000000..a3212d2 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBounds.wacc @@ -0,0 +1,16 @@ +# attempt out of bounds array access (this ought to seg fault or similar) + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + + int[] b = [1, 2, 3] ; + int[] a = [43, 2, 18, 1] ; + println a[5] +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBoundsWrite.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBoundsWrite.wacc new file mode 100644 index 0000000..1288bda --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/arrayOutOfBounds/arrayOutOfBoundsWrite.wacc @@ -0,0 +1,17 @@ +# attempt write out of array bounds (gods, this really should not work!) + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + + int[] b = [1, 2, 3] ; + int[] a = [43, 2, 18, 1] ; + a[5] = 100 ; + println a[5] +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/badChar/negativeChr.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/badChar/negativeChr.wacc new file mode 100644 index 0000000..28bad3a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/badChar/negativeChr.wacc @@ -0,0 +1,13 @@ +# try to convert negative int to character + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + char c = chr -1 +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/badChar/tooBigChr.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/badChar/tooBigChr.wacc new file mode 100644 index 0000000..698e9e1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/badChar/tooBigChr.wacc @@ -0,0 +1,13 @@ +# try to convert a too large number to character + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + char c = chr 128 +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divZero.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divZero.wacc new file mode 100644 index 0000000..d7c83d9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divZero.wacc @@ -0,0 +1,14 @@ +# division by zero + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = 10 / 0 ; + println "should not reach here" +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divideByZero.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divideByZero.wacc new file mode 100644 index 0000000..89d4501 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/divideByZero.wacc @@ -0,0 +1,15 @@ +# attempt divide by zero + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = 10 ; + int y = 0 ; + print x / y +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/modByZero.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/modByZero.wacc new file mode 100644 index 0000000..9ba07c9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/divideByZero/modByZero.wacc @@ -0,0 +1,15 @@ +# attempt modulo by zero + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = 10 ; + int y = 0 ; + print x % y +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intJustOverflow.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intJustOverflow.wacc new file mode 100644 index 0000000..9b1e39b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intJustOverflow.wacc @@ -0,0 +1,20 @@ +# integer overflow + +# Output: +# 2147483646 +# 2147483647 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = 2147483646 ; + println x ; + x = x + 1 ; + println x ; + x = x + 1 ; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intUnderflow.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intUnderflow.wacc new file mode 100644 index 0000000..7127c54 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intUnderflow.wacc @@ -0,0 +1,20 @@ +# integer underflow + +# Output: +# -2147483647 +# -2147483648 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = -2147483647 ; + println x ; + x = x - 1 ; + println x ; + x = x - 1 ; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intWayOverflow.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intWayOverflow.wacc new file mode 100644 index 0000000..d0ce0ec --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intWayOverflow.wacc @@ -0,0 +1,17 @@ +# positive overflow + +# Output: +# 2000000000 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = 2000000000 ; + println x ; + x = x + 2000000000; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intmultOverflow.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intmultOverflow.wacc new file mode 100644 index 0000000..6d81bc7 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intmultOverflow.wacc @@ -0,0 +1,22 @@ +# integer overflow - generates odd assembly error! + +# Output: +# 2147483 +# 2147483000 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = 2147483 ; + println x ; + x = x * 1000 ; + println x ; + x = x * 1000 ; + println x ; + x = x * 1000 ; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow.wacc new file mode 100644 index 0000000..c0a4a1d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow.wacc @@ -0,0 +1,17 @@ +# negating biggest possible negative integer + +# Output: +# -2147483648 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = -2147483648 ; + println x ; + x = -x; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow2.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow2.wacc new file mode 100644 index 0000000..3ad8c96 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow2.wacc @@ -0,0 +1,17 @@ +# multiplying the biggest possible negative integer + +# Output: +# -2147483648 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = -2147483648 ; + println x ; + x = x*10; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow3.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow3.wacc new file mode 100644 index 0000000..2ed0726 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow3.wacc @@ -0,0 +1,17 @@ +# multiplying a negative integer + +# Output: +# -20000 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = -20000 ; + println x ; + x = x*100000000; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow4.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow4.wacc new file mode 100644 index 0000000..b12f3d3 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/integerOverflow/intnegateOverflow4.wacc @@ -0,0 +1,17 @@ +# negative overflow + +# Output: +# -2000000000 +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + int x = -2000000000 ; + println x ; + x = x - 2000000000; #err here? + println x +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/freeNull.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/freeNull.wacc new file mode 100644 index 0000000..afb1a03 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/freeNull.wacc @@ -0,0 +1,15 @@ +# Create and free a null pair + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + pair(pair, pair) a = null ; + free a +end + diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull1.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull1.wacc new file mode 100644 index 0000000..b1b872f --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull1.wacc @@ -0,0 +1,14 @@ +# attempt dereference of a null pair by reading into an element of it + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + pair(int, int) p = null ; + read fst p +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull2.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull2.wacc new file mode 100644 index 0000000..2042aa6 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/readNull2.wacc @@ -0,0 +1,14 @@ +# attempt dereference of a null pair by reading into an element of it + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + pair(int, int) p = null ; + read snd p +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull1.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull1.wacc new file mode 100644 index 0000000..4ddb1ce --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull1.wacc @@ -0,0 +1,14 @@ +# attempt dereference of a null pair by setting an element of it + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + pair(int, int) p = null ; + fst p = 1 +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull2.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull2.wacc new file mode 100644 index 0000000..60fd7d1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/setNull2.wacc @@ -0,0 +1,14 @@ +# attempt dereference of a null pair by setting an element of it + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + pair(int, int) p = null ; + snd p = 1 +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull1.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull1.wacc new file mode 100644 index 0000000..e8549fd --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull1.wacc @@ -0,0 +1,14 @@ +# attempt dereference of a null pair by using it + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + pair(int, int) p = null ; + int x = fst p +end diff --git a/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull2.wacc b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull2.wacc new file mode 100644 index 0000000..c0f550a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/runtimeErr/nullDereference/useNull2.wacc @@ -0,0 +1,14 @@ +# attempt dereference of a null pair by using it + +# Output: +# #runtime_error# + +# Exit: +# 255 + +# Program: + +begin + pair(int, int) p = null ; + int x = snd p +end diff --git a/src/test/wacc/waccPrograms/valid/scope/ifNested1.wacc b/src/test/wacc/waccPrograms/valid/scope/ifNested1.wacc new file mode 100644 index 0000000..e6db3b5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/ifNested1.wacc @@ -0,0 +1,22 @@ +# Nested conditional statement + +# Output: +# correct +# + +# Program: + +begin + int a = 13; + if a == 13 + then + if a > 5 + then + println "correct" + else + println "incorrect" + fi + else + println "incorrect" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/scope/ifNested2.wacc b/src/test/wacc/waccPrograms/valid/scope/ifNested2.wacc new file mode 100644 index 0000000..00b1227 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/ifNested2.wacc @@ -0,0 +1,37 @@ +# Deeply nested conditional statement + +# Output: +# correct +# + +# Program: + +begin + int a = 13; + if a == 13 + then + if a > 5 + then + if a < 10 + then + println "incorrect" + else + if a > 12 + then + if a > 13 + then + println "incorrect" + else + println "correct" + fi + else + println "incorrect" + fi + fi + else + println "incorrect" + fi + else + println "incorrect" + fi +end diff --git a/src/test/wacc/waccPrograms/valid/scope/indentationNotImportant.wacc b/src/test/wacc/waccPrograms/valid/scope/indentationNotImportant.wacc new file mode 100644 index 0000000..7adc97c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/indentationNotImportant.wacc @@ -0,0 +1,13 @@ +# consistent indentation is for readability purposes only + +# Output: + +# Program: + + begin + while +false + do + skip +done + end diff --git a/src/test/wacc/waccPrograms/valid/scope/intsAndKeywords.wacc b/src/test/wacc/waccPrograms/valid/scope/intsAndKeywords.wacc new file mode 100644 index 0000000..f43f11a --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/intsAndKeywords.wacc @@ -0,0 +1,9 @@ +# checking handling of ints and keywords + +# Output: + +# Program: +begin + begin + int x = 125end +end diff --git a/src/test/wacc/waccPrograms/valid/scope/printAllTypes.wacc b/src/test/wacc/waccPrograms/valid/scope/printAllTypes.wacc new file mode 100644 index 0000000..b7c493d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/printAllTypes.wacc @@ -0,0 +1,126 @@ +# Tests scoping with most variable types + +# Output: +# ( [1, 2, 3] , [a, b, c] ) +# [ #addrs# = (a, true), #addrs# = (b, false) ] +# 1, 2 +# array, of, strings +# true, false, true +# xyz +# 1, 2, 3 +# this is a string +# true +# x +# 5 +# + +# Program: + +begin + + string comma = ", "; + int x = 5; + begin + char x = 'x'; + begin + bool x = true; + begin + string x = "this is a string"; + begin + int[] x = [1,2,3]; + begin + char[] x = ['x', 'y', 'z']; + begin + bool[] x = [true, false, true]; + begin + string[] x = ["array", "of", "strings"]; + begin + pair(int, int) x = newpair(1, 2); + begin + pair(char, bool) y = newpair('a', true); + pair(char, bool) z = newpair('b', false); + pair(char, bool)[] x = [y, z]; + begin + int[] y = [1, 2, 3]; + char[] z = ['a', 'b', 'c']; + pair(int[], char[]) x = newpair(y, z); + begin + skip + end; + int[] a = fst x; + char[] b = snd x; + print "( ["; + print a[0]; + print comma; + print a[1]; + print comma; + print a[2]; + print "] , ["; + print b[0]; + print comma; + print b[1]; + print comma; + print b[2]; + println "] )" # ( [fst x] , [snd x] ) + end; + pair(char, bool) a = x[0]; + char aa = fst a; + bool ab = snd a; + pair(char, bool) b = x[1]; + char ba = fst b; + bool bb = snd b; + print "[ "; + print a; + print " = ("; + print aa; + print comma; + print ab; + print "), "; + print b; + print " = ("; + print ba; + print comma; + print bb; + println ") ]" # [ x[0], x[1] ] + end; + int y = fst x; + int z = snd x; + print y; + print comma; + println z # fst x, snd x + end; + string a = x[0]; + string b = x[1]; + string c = x[2]; + print a; + print comma; + print b; + print comma; + println c # string[] + end; + print x[0]; + print comma; + print x[1]; + print comma; + println x[2] # bool[] + end; + println x # char[] + end; + int a = x[0]; + int b = x[1]; + int c = x[2]; + print a; + print comma; + print b; + print comma; + println c # int[] + end; + println x # string + end; + println x # bool + end; + println x # char + end; + println x # int + +end \ No newline at end of file diff --git a/src/test/wacc/waccPrograms/valid/scope/scope.wacc b/src/test/wacc/waccPrograms/valid/scope/scope.wacc new file mode 100644 index 0000000..ad87e34 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scope.wacc @@ -0,0 +1,18 @@ +# simple scoping test + +# Output: + +# Program: + +begin + begin + begin + begin + begin + skip + end + end + end + end +end + diff --git a/src/test/wacc/waccPrograms/valid/scope/scopeBasic.wacc b/src/test/wacc/waccPrograms/valid/scope/scopeBasic.wacc new file mode 100644 index 0000000..1128fe8 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scopeBasic.wacc @@ -0,0 +1,13 @@ +# very simple scoping test + +# Output: + +# Program: + +begin + skip; + begin + skip + end +end + diff --git a/src/test/wacc/waccPrograms/valid/scope/scopeIfRedefine.wacc b/src/test/wacc/waccPrograms/valid/scope/scopeIfRedefine.wacc new file mode 100644 index 0000000..373c364 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scopeIfRedefine.wacc @@ -0,0 +1,20 @@ +# variable scoping test that redefines a variable within an if-statement + +# Output: +# true +# 12 +# + +# Program: + +begin + int x = 12 ; + if x == 12 then + bool x = true ; + println x + else + char x = 'a'; + println x + fi ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/scope/scopeRedefine.wacc b/src/test/wacc/waccPrograms/valid/scope/scopeRedefine.wacc new file mode 100644 index 0000000..82e1963 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scopeRedefine.wacc @@ -0,0 +1,18 @@ +# more complex variable scoping test that redefines a variable + +# Output: +# true +# 2 +# + +# Program: + +begin + int x = 1 ; + begin + x = 2 ; + bool x = true ; + println x + end ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/scope/scopeSimpleRedefine.wacc b/src/test/wacc/waccPrograms/valid/scope/scopeSimpleRedefine.wacc new file mode 100644 index 0000000..b299aed --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scopeSimpleRedefine.wacc @@ -0,0 +1,17 @@ +# variable scoping test that redefines a variable + +# Output: +# true +# 12 +# + +# Program: + +begin + int x = 12 ; + begin + bool x = true ; + println x + end ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/scope/scopeVars.wacc b/src/test/wacc/waccPrograms/valid/scope/scopeVars.wacc new file mode 100644 index 0000000..b0b8a5e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scopeVars.wacc @@ -0,0 +1,19 @@ +# simple variable scoping test + +# Output: +# 2 +# 4 +# 2 +# + +# Program: + +begin + int x = 2 ; + println x ; + begin + int x = 4 ; + println x + end ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/scope/scopeWhileNested.wacc b/src/test/wacc/waccPrograms/valid/scope/scopeWhileNested.wacc new file mode 100644 index 0000000..f241031 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scopeWhileNested.wacc @@ -0,0 +1,26 @@ +# variable scoping nested within a while-loop + +# Output: +# counting... 5 +# counting... 4 +# counting... 3 +# counting... 2 +# counting... 1 +# 0 Boom! +# + +# Program: + +begin + int x = 5 ; + while x > 0 do + begin + string x = "counting... " ; + print x + end ; + println x ; + x = x - 1 + done ; + print x ; + println " Boom!" +end diff --git a/src/test/wacc/waccPrograms/valid/scope/scopeWhileRedefine.wacc b/src/test/wacc/waccPrograms/valid/scope/scopeWhileRedefine.wacc new file mode 100644 index 0000000..16cbd7e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/scopeWhileRedefine.wacc @@ -0,0 +1,25 @@ +# variable scoping test that redefines a variable within a while-loop + +# Output: +# counting... 5 +# counting... 4 +# counting... 3 +# counting... 2 +# counting... 1 +# 0 Boom! +# + +# Program: + +begin + int x = 5 ; + string y = " Boom!" ; + while x > 0 do + string y = "counting... " ; + print y; + println x; + x = x - 1 + done ; + print x; + println y +end diff --git a/src/test/wacc/waccPrograms/valid/scope/splitScope.wacc b/src/test/wacc/waccPrograms/valid/scope/splitScope.wacc new file mode 100644 index 0000000..bc1f759 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/scope/splitScope.wacc @@ -0,0 +1,19 @@ +# splits the first appearances of variables by a new scope to ensure proper grouping + +# Output: +# 3 +# 2 +# + +# Program: + +begin + int x = 1 ; + begin + x = 3 ; + bool z = true + end ; + int y = 2 ; + println x ; + println y +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/basicSeq.wacc b/src/test/wacc/waccPrograms/valid/sequence/basicSeq.wacc new file mode 100644 index 0000000..46fbd3e --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/basicSeq.wacc @@ -0,0 +1,10 @@ +# basic sequential composition + +# Output: + +# Program: + +begin + skip; + skip +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/basicSeq2.wacc b/src/test/wacc/waccPrograms/valid/sequence/basicSeq2.wacc new file mode 100644 index 0000000..9f8fff6 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/basicSeq2.wacc @@ -0,0 +1,11 @@ +# basic sequential composition + +# Output: + +# Program: + +begin + skip; + skip; + skip +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/boolAssignment.wacc b/src/test/wacc/waccPrograms/valid/sequence/boolAssignment.wacc new file mode 100644 index 0000000..862410d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/boolAssignment.wacc @@ -0,0 +1,10 @@ +# simple boolean variable declaration and assignment + +# Output: + +# Program: + +begin + bool b = false ; + b = true +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/charAssignment.wacc b/src/test/wacc/waccPrograms/valid/sequence/charAssignment.wacc new file mode 100644 index 0000000..bfeed4c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/charAssignment.wacc @@ -0,0 +1,10 @@ +# simple character variable declaration and assignment + +# Output: + +# Program: + +begin + char c = 'a' ; + c = 'Z' +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/exitSimple.wacc b/src/test/wacc/waccPrograms/valid/sequence/exitSimple.wacc new file mode 100644 index 0000000..c4a1482 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/exitSimple.wacc @@ -0,0 +1,13 @@ +# exit with unreachable print + +# Output: + +# Exit: +# 42 + +# Program: + +begin + exit 42 ; + println "Should not print this." +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/intAssignment.wacc b/src/test/wacc/waccPrograms/valid/sequence/intAssignment.wacc new file mode 100644 index 0000000..1dc130d --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/intAssignment.wacc @@ -0,0 +1,14 @@ +# simple integer variable declaration and assignment + +# Output: + +# Exit: +# 20 + +# Program: + +begin + int x = 10 ; + x = 20 ; + exit x +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/intLeadingZeros.wacc b/src/test/wacc/waccPrograms/valid/sequence/intLeadingZeros.wacc new file mode 100644 index 0000000..0ede809 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/intLeadingZeros.wacc @@ -0,0 +1,15 @@ +# integer variable declaration with leading zeroes + +# Output: +# 42 +# 0 +# + +# Program: + +begin + int x = 0000000000000000000000000000000000000042 ; + int y = 0000000000000000000000000000000000000000 ; + println x ; + println y +end diff --git a/src/test/wacc/waccPrograms/valid/sequence/stringAssignment.wacc b/src/test/wacc/waccPrograms/valid/sequence/stringAssignment.wacc new file mode 100644 index 0000000..e250f74 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/sequence/stringAssignment.wacc @@ -0,0 +1,10 @@ +# simple string variable declaration and assignment + +# Output: + +# Program: + +begin + string s = "foo" ; + s = "bar" +end diff --git a/src/test/wacc/waccPrograms/valid/variables/_VarNames.wacc b/src/test/wacc/waccPrograms/valid/variables/_VarNames.wacc new file mode 100644 index 0000000..fba0457 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/_VarNames.wacc @@ -0,0 +1,13 @@ +# variable can have _ in their names + +# Output: + +# Exit: +# 19 + +# Program: + +begin + int an_underscore = 19 ; + exit an_underscore +end diff --git a/src/test/wacc/waccPrograms/valid/variables/boolDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/boolDeclaration.wacc new file mode 100644 index 0000000..ac152e0 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/boolDeclaration.wacc @@ -0,0 +1,9 @@ +# simple boolean variable declaration + +# Output: + +# Program: + +begin + bool b = false +end diff --git a/src/test/wacc/waccPrograms/valid/variables/boolDeclaration2.wacc b/src/test/wacc/waccPrograms/valid/variables/boolDeclaration2.wacc new file mode 100644 index 0000000..cf3a48b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/boolDeclaration2.wacc @@ -0,0 +1,9 @@ +# simple true boolean variable declaration + +# Output: + +# Program: + +begin + bool b = true +end diff --git a/src/test/wacc/waccPrograms/valid/variables/capCharDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/capCharDeclaration.wacc new file mode 100644 index 0000000..97b9e82 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/capCharDeclaration.wacc @@ -0,0 +1,9 @@ +# simple capital character variable declaration + +# Output: + +# Program: + +begin + char c = 'M' +end diff --git a/src/test/wacc/waccPrograms/valid/variables/charDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/charDeclaration.wacc new file mode 100644 index 0000000..9c06831 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/charDeclaration.wacc @@ -0,0 +1,9 @@ +# simple character variable declaration + +# Output: + +# Program: + +begin + char c = 'a' +end diff --git a/src/test/wacc/waccPrograms/valid/variables/charDeclaration2.wacc b/src/test/wacc/waccPrograms/valid/variables/charDeclaration2.wacc new file mode 100644 index 0000000..0e5ba2c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/charDeclaration2.wacc @@ -0,0 +1,9 @@ +# simple character variable declaration + +# Output: + +# Program: + +begin + char c = 'z' +end diff --git a/src/test/wacc/waccPrograms/valid/variables/emptyStringDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/emptyStringDeclaration.wacc new file mode 100644 index 0000000..c7b1e41 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/emptyStringDeclaration.wacc @@ -0,0 +1,9 @@ +# simple empty string variable declaration + +# Output: + +# Program: + +begin + string s = "" +end diff --git a/src/test/wacc/waccPrograms/valid/variables/intDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/intDeclaration.wacc new file mode 100644 index 0000000..c2acab5 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/intDeclaration.wacc @@ -0,0 +1,9 @@ +# simple integer variable declaration + +# Output: + +# Program: + +begin + int x = 42 +end diff --git a/src/test/wacc/waccPrograms/valid/variables/longVarNames.wacc b/src/test/wacc/waccPrograms/valid/variables/longVarNames.wacc new file mode 100644 index 0000000..5cad032 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/longVarNames.wacc @@ -0,0 +1,13 @@ +# variable can have very long names + +# Output: + +# Exit: +# 5 + +# Program: + +begin + int this_variable_has_a_crazy_long_name_but_ought_to_still_be_valid = 5 ; + exit this_variable_has_a_crazy_long_name_but_ought_to_still_be_valid +end diff --git a/src/test/wacc/waccPrograms/valid/variables/manyVariables.wacc b/src/test/wacc/waccPrograms/valid/variables/manyVariables.wacc new file mode 100644 index 0000000..dbdbad2 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/manyVariables.wacc @@ -0,0 +1,265 @@ +# creates 257 variables + +# Output: + +# Program: + +begin +int x0 = 0 ; +int x1 = 1 ; +int x2 = 2 ; +int x3 = 3 ; +int x4 = 4 ; +int x5 = 5 ; +int x6 = 6 ; +int x7 = 7 ; +int x8 = 8 ; +int x9 = 9 ; +int x10 = 10 ; +int x11 = 11 ; +int x12 = 12 ; +int x13 = 13 ; +int x14 = 14 ; +int x15 = 15 ; +int x16 = 16 ; +int x17 = 17 ; +int x18 = 18 ; +int x19 = 19 ; +int x20 = 20 ; +int x21 = 21 ; +int x22 = 22 ; +int x23 = 23 ; +int x24 = 24 ; +int x25 = 25 ; +int x26 = 26 ; +int x27 = 27 ; +int x28 = 28 ; +int x29 = 29 ; +int x30 = 30 ; +int x31 = 31 ; +int x32 = 32 ; +int x33 = 33 ; +int x34 = 34 ; +int x35 = 35 ; +int x36 = 36 ; +int x37 = 37 ; +int x38 = 38 ; +int x39 = 39 ; +int x40 = 40 ; +int x41 = 41 ; +int x42 = 42 ; +int x43 = 43 ; +int x44 = 44 ; +int x45 = 45 ; +int x46 = 46 ; +int x47 = 47 ; +int x48 = 48 ; +int x49 = 49 ; +int x50 = 50 ; +int x51 = 51 ; +int x52 = 52 ; +int x53 = 53 ; +int x54 = 54 ; +int x55 = 55 ; +int x56 = 56 ; +int x57 = 57 ; +int x58 = 58 ; +int x59 = 59 ; +int x60 = 60 ; +int x61 = 61 ; +int x62 = 62 ; +int x63 = 63 ; +int x64 = 64 ; +int x65 = 65 ; +int x66 = 66 ; +int x67 = 67 ; +int x68 = 68 ; +int x69 = 69 ; +int x70 = 70 ; +int x71 = 71 ; +int x72 = 72 ; +int x73 = 73 ; +int x74 = 74 ; +int x75 = 75 ; +int x76 = 76 ; +int x77 = 77 ; +int x78 = 78 ; +int x79 = 79 ; +int x80 = 80 ; +int x81 = 81 ; +int x82 = 82 ; +int x83 = 83 ; +int x84 = 84 ; +int x85 = 85 ; +int x86 = 86 ; +int x87 = 87 ; +int x88 = 88 ; +int x89 = 89 ; +int x90 = 90 ; +int x91 = 91 ; +int x92 = 92 ; +int x93 = 93 ; +int x94 = 94 ; +int x95 = 95 ; +int x96 = 96 ; +int x97 = 97 ; +int x98 = 98 ; +int x99 = 99 ; +int x100 = 100 ; +int x101 = 101 ; +int x102 = 102 ; +int x103 = 103 ; +int x104 = 104 ; +int x105 = 105 ; +int x106 = 106 ; +int x107 = 107 ; +int x108 = 108 ; +int x109 = 109 ; +int x110 = 110 ; +int x111 = 111 ; +int x112 = 112 ; +int x113 = 113 ; +int x114 = 114 ; +int x115 = 115 ; +int x116 = 116 ; +int x117 = 117 ; +int x118 = 118 ; +int x119 = 119 ; +int x120 = 120 ; +int x121 = 121 ; +int x122 = 122 ; +int x123 = 123 ; +int x124 = 124 ; +int x125 = 125 ; +int x126 = 126 ; +int x127 = 127 ; +int x128 = 128 ; +int x129 = 129 ; +int x130 = 130 ; +int x131 = 131 ; +int x132 = 132 ; +int x133 = 133 ; +int x134 = 134 ; +int x135 = 135 ; +int x136 = 136 ; +int x137 = 137 ; +int x138 = 138 ; +int x139 = 139 ; +int x140 = 140 ; +int x141 = 141 ; +int x142 = 142 ; +int x143 = 143 ; +int x144 = 144 ; +int x145 = 145 ; +int x146 = 146 ; +int x147 = 147 ; +int x148 = 148 ; +int x149 = 149 ; +int x150 = 150 ; +int x151 = 151 ; +int x152 = 152 ; +int x153 = 153 ; +int x154 = 154 ; +int x155 = 155 ; +int x156 = 156 ; +int x157 = 157 ; +int x158 = 158 ; +int x159 = 159 ; +int x160 = 160 ; +int x161 = 161 ; +int x162 = 162 ; +int x163 = 163 ; +int x164 = 164 ; +int x165 = 165 ; +int x166 = 166 ; +int x167 = 167 ; +int x168 = 168 ; +int x169 = 169 ; +int x170 = 170 ; +int x171 = 171 ; +int x172 = 172 ; +int x173 = 173 ; +int x174 = 174 ; +int x175 = 175 ; +int x176 = 176 ; +int x177 = 177 ; +int x178 = 178 ; +int x179 = 179 ; +int x180 = 180 ; +int x181 = 181 ; +int x182 = 182 ; +int x183 = 183 ; +int x184 = 184 ; +int x185 = 185 ; +int x186 = 186 ; +int x187 = 187 ; +int x188 = 188 ; +int x189 = 189 ; +int x190 = 190 ; +int x191 = 191 ; +int x192 = 192 ; +int x193 = 193 ; +int x194 = 194 ; +int x195 = 195 ; +int x196 = 196 ; +int x197 = 197 ; +int x198 = 198 ; +int x199 = 199 ; +int x200 = 200 ; +int x201 = 201 ; +int x202 = 202 ; +int x203 = 203 ; +int x204 = 204 ; +int x205 = 205 ; +int x206 = 206 ; +int x207 = 207 ; +int x208 = 208 ; +int x209 = 209 ; +int x210 = 210 ; +int x211 = 211 ; +int x212 = 212 ; +int x213 = 213 ; +int x214 = 214 ; +int x215 = 215 ; +int x216 = 216 ; +int x217 = 217 ; +int x218 = 218 ; +int x219 = 219 ; +int x220 = 220 ; +int x221 = 221 ; +int x222 = 222 ; +int x223 = 223 ; +int x224 = 224 ; +int x225 = 225 ; +int x226 = 226 ; +int x227 = 227 ; +int x228 = 228 ; +int x229 = 229 ; +int x230 = 230 ; +int x231 = 231 ; +int x232 = 232 ; +int x233 = 233 ; +int x234 = 234 ; +int x235 = 235 ; +int x236 = 236 ; +int x237 = 237 ; +int x238 = 238 ; +int x239 = 239 ; +int x240 = 240 ; +int x241 = 241 ; +int x242 = 242 ; +int x243 = 243 ; +int x244 = 244 ; +int x245 = 245 ; +int x246 = 246 ; +int x247 = 247 ; +int x248 = 248 ; +int x249 = 249 ; +int x250 = 250 ; +int x251 = 251 ; +int x252 = 252 ; +int x253 = 253 ; +int x254 = 254 ; +int x255 = 255 ; +int x256 = 256 +end diff --git a/src/test/wacc/waccPrograms/valid/variables/negIntDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/negIntDeclaration.wacc new file mode 100644 index 0000000..8c1f777 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/negIntDeclaration.wacc @@ -0,0 +1,9 @@ +# simple negative integer variable declaration + +# Output: + +# Program: + +begin + int x = -1 +end diff --git a/src/test/wacc/waccPrograms/valid/variables/puncCharDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/puncCharDeclaration.wacc new file mode 100644 index 0000000..57d6b64 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/puncCharDeclaration.wacc @@ -0,0 +1,9 @@ +# simple punctuation character variable declaration + +# Output: + +# Program: + +begin + char c = '!' +end diff --git a/src/test/wacc/waccPrograms/valid/variables/stringCarriageReturn.wacc b/src/test/wacc/waccPrograms/valid/variables/stringCarriageReturn.wacc new file mode 100644 index 0000000..48b3d6b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/stringCarriageReturn.wacc @@ -0,0 +1,9 @@ +# carriage returns should be parsable, but their behaviour is non-portable, so the IO is not currently tested + +# Output: + +# Program: + +begin + string s = "Hello \r World!\n" +end diff --git a/src/test/wacc/waccPrograms/valid/variables/stringDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/stringDeclaration.wacc new file mode 100644 index 0000000..82fb71c --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/stringDeclaration.wacc @@ -0,0 +1,9 @@ +# simple string variable declaration + +# Output: + +# Program: + +begin + string s = "Hello World!" +end diff --git a/src/test/wacc/waccPrograms/valid/variables/zeroIntDeclaration.wacc b/src/test/wacc/waccPrograms/valid/variables/zeroIntDeclaration.wacc new file mode 100644 index 0000000..564cab1 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/variables/zeroIntDeclaration.wacc @@ -0,0 +1,9 @@ +# simple zero integer variable declaration + +# Output: + +# Program: + +begin + int x = 0 +end diff --git a/src/test/wacc/waccPrograms/valid/while/fibonacciFullIt.wacc b/src/test/wacc/waccPrograms/valid/while/fibonacciFullIt.wacc new file mode 100644 index 0000000..40596ca --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/fibonacciFullIt.wacc @@ -0,0 +1,31 @@ +# iteratively calculate the given fibonacci number + +# Input: 30 + +# Output: +# This program calculates the nth fibonacci number iteratively. +# Please enter n (should not be too large): The input n is 30 +# The nth fibonacci number is 832040 +# + +# Program: + +begin + println "This program calculates the nth fibonacci number iteratively." ; + print "Please enter n (should not be too large): " ; + int n = 0; + read n ; + print "The input n is " ; + println n ; + print "The nth fibonacci number is " ; + int f0 = 0 ; + int f1 = 1 ; + int save = 0; + while n > 0 do + save = f0 ; + f0 = f1 ; + f1 = save + f1 ; + n = n - 1 + done ; + println f0 +end diff --git a/src/test/wacc/waccPrograms/valid/while/fibonacciIterative.wacc b/src/test/wacc/waccPrograms/valid/while/fibonacciIterative.wacc new file mode 100644 index 0000000..7132f73 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/fibonacciIterative.wacc @@ -0,0 +1,25 @@ +# iterative calculation of the first 20 fibonacci numbers + +# Output: +# The first 20 fibonacci numbers are: +# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, ... +# + +# Program: + +begin + int i = 0 ; + int f0 = 0 ; + int f1 = 1 ; + int save = 0; + println "The first 20 fibonacci numbers are:" ; + while i < 20 do + print f0 ; + print ", " ; + save = f0 ; + f0 = f1 ; + f1 = save + f1 ; + i = i + 1 + done ; + println "..." +end diff --git a/src/test/wacc/waccPrograms/valid/while/loopCharCondition.wacc b/src/test/wacc/waccPrograms/valid/while/loopCharCondition.wacc new file mode 100644 index 0000000..170e359 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/loopCharCondition.wacc @@ -0,0 +1,17 @@ +# Use a character as a loop condition. Enter the loop once only, then exit the loop. + +# Output: +# Change c +# Should print "Change c" once before. +# + +# Program: + +begin + char c = '\0' ; + while c == '\0' do + c = 'a' ; + println "Change c" + done ; + println "Should print \"Change c\" once before." +end diff --git a/src/test/wacc/waccPrograms/valid/while/loopIntCondition.wacc b/src/test/wacc/waccPrograms/valid/while/loopIntCondition.wacc new file mode 100644 index 0000000..09f4271 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/loopIntCondition.wacc @@ -0,0 +1,17 @@ +# Use an integer as a loop condition. Enter the loop once only, then exit the loop. + +# Output: +# Change n +# Should print "Change n" once before. +# + +# Program: + +begin + int n = 0 ; + while n != 1 do + n = 1 ; + println "Change n" + done ; + println "Should print \"Change n\" once before." +end diff --git a/src/test/wacc/waccPrograms/valid/while/max.wacc b/src/test/wacc/waccPrograms/valid/while/max.wacc new file mode 100644 index 0000000..11a54cd --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/max.wacc @@ -0,0 +1,20 @@ +# find the max of two numbers + +# Output: +# max value = 17 +# + +# Program: + +begin + int i = 0 ; + int x = 10 ; + int y = 17 ; + while (y > 0 || x > 0) do + x = x - 1 ; + y = y - 1 ; + i = i + 1 + done ; + print "max value = "; + println i +end diff --git a/src/test/wacc/waccPrograms/valid/while/min.wacc b/src/test/wacc/waccPrograms/valid/while/min.wacc new file mode 100644 index 0000000..57f9564 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/min.wacc @@ -0,0 +1,20 @@ +# find the min of two numbers + +# Output: +# min value = 10 +# + +# Program: + +begin + int i = 0 ; + int x = 10 ; + int y = 17 ; + while y > 0 && x > 0 do + x = x - 1 ; + y = y - 1 ; + i = i + 1 + done ; + print "min value = " ; + println i +end diff --git a/src/test/wacc/waccPrograms/valid/while/rmStyleAdd.wacc b/src/test/wacc/waccPrograms/valid/while/rmStyleAdd.wacc new file mode 100644 index 0000000..a252325 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/rmStyleAdd.wacc @@ -0,0 +1,24 @@ +# register machine style addition + +# Output: +# initial value of x: 3 +# (+)(+)(+)(+)(+)(+)(+) +# final value of x: 10 +# + +# Program: + +begin + int x = 3 ; + int y = 7 ; + print "initial value of x: " ; + println x ; + while y > 0 do + print "(+)" ; + x = x + 1 ; + y = y - 1 + done ; + println "" ; + print "final value of x: " ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/while/rmStyleAddIO.wacc b/src/test/wacc/waccPrograms/valid/while/rmStyleAddIO.wacc new file mode 100644 index 0000000..d531bd9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/rmStyleAddIO.wacc @@ -0,0 +1,30 @@ +# register machine style addition + +# Input: 2 40 + +# Output: +# Enter the first number: Enter the second number: Initial value of x: 2 +# (+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+)(+) +# final value of x: 42 +# + +# Program: + +begin + int x = 0 ; + int y = 0 ; + print "Enter the first number: " ; + read x ; + print "Enter the second number: " ; + read y ; + print "Initial value of x: " ; + println x ; + while y > 0 do + print "(+)" ; + x = x + 1 ; + y = y - 1 + done ; + println "" ; + print "final value of x: " ; + println x +end diff --git a/src/test/wacc/waccPrograms/valid/while/whileBasic.wacc b/src/test/wacc/waccPrograms/valid/while/whileBasic.wacc new file mode 100644 index 0000000..3cd381b --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/whileBasic.wacc @@ -0,0 +1,11 @@ +# simple while loop + +# Output: + +# Program: + +begin + while false do + skip + done +end diff --git a/src/test/wacc/waccPrograms/valid/while/whileBoolFlip.wacc b/src/test/wacc/waccPrograms/valid/while/whileBoolFlip.wacc new file mode 100644 index 0000000..ef3d5c9 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/whileBoolFlip.wacc @@ -0,0 +1,17 @@ +# while loop flips bool to terminate + +# Output: +# flip b! +# end of loop +# + +# Program: + +begin + bool b = true ; + while b do + println "flip b!" ; + b = !b + done ; + println "end of loop" +end diff --git a/src/test/wacc/waccPrograms/valid/while/whileCount.wacc b/src/test/wacc/waccPrograms/valid/while/whileCount.wacc new file mode 100644 index 0000000..95973c6 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/whileCount.wacc @@ -0,0 +1,26 @@ +# simple counting while loop + +# Output: +# Can you count to 10? +# 1 +# 2 +# 3 +# 4 +# 5 +# 6 +# 7 +# 8 +# 9 +# 10 +# + +# Program: + +begin + int i = 1 ; + println "Can you count to 10?" ; + while i <= 10 do + println i ; + i = i + 1 + done +end diff --git a/src/test/wacc/waccPrograms/valid/while/whileFalse.wacc b/src/test/wacc/waccPrograms/valid/while/whileFalse.wacc new file mode 100644 index 0000000..a0734a7 --- /dev/null +++ b/src/test/wacc/waccPrograms/valid/while/whileFalse.wacc @@ -0,0 +1,14 @@ +# simple unentered while loop + +# Output: +# end of loop +# + +# Program: + +begin + while false do + println "looping..." + done ; + println "end of loop" +end