diff --git a/Test/JavaSources/Main.java b/Test/JavaSources/Main.java index b79d383..02bfcc2 100644 --- a/Test/JavaSources/Main.java +++ b/Test/JavaSources/Main.java @@ -18,6 +18,7 @@ public class Main { TestLoop loop = new TestLoop(); TestMethodOverload overload = new TestMethodOverload(); TestShenanigance shenanigance = new TestShenanigance(); + TestOptionalParameter optionalParameter = new TestOptionalParameter(); // constructing a basic class works assert empty != null; @@ -50,8 +51,14 @@ public class Main { { assert malicious.cursedFormatting(i) == i; } + // other syntactic sugar assert shenanigance.testAssignment() == 5; assert shenanigance.divEqual() == 234_343_000 / 4; assert shenanigance.testIf(5); + // optional parameters + assert optionalParameter.oneOptional() == 1; + assert optionalParameter.oneOptional(2) == 2; + assert optionalParameter.normalAndOptional(1) == 6; + assert optionalParameter.normalAndOptional(1, 0) == 4; } } diff --git a/Test/JavaSources/TestOptionalParameter.java b/Test/JavaSources/TestOptionalParameter.java new file mode 100644 index 0000000..faba782 --- /dev/null +++ b/Test/JavaSources/TestOptionalParameter.java @@ -0,0 +1,10 @@ +class TestOptionalParameter { + + int oneOptional(int p = 1) { + return p; + } + + int normalAndOptional(int a, int b = 2, int c = 3) { + return a + b + c; + } +} \ No newline at end of file diff --git a/src/Parser/JavaParser.y b/src/Parser/JavaParser.y index 0b20ce7..841d68c 100644 --- a/src/Parser/JavaParser.y +++ b/src/Parser/JavaParser.y @@ -1,5 +1,5 @@ { -module Parser.JavaParser (parse, parseStatement, parseExpression) where +module Parser.JavaParser (parse, parseStatement, parseExpression, parseMethod) where import Ast import Parser.Lexer } @@ -7,6 +7,7 @@ import Parser.Lexer %name parse %name parseStatement statement %name parseExpression expression +%name parseMethod classbodydeclarations %tokentype { Token } %error { parseError } %errorhandlertype explist @@ -104,13 +105,13 @@ modifiers : modifier { } classbodydeclarations : classbodydeclaration { case $1 of ConstructorDecl constructor -> ([constructor], [], []) - MethodDecl method -> ([], [method], []) + MethodDecl method -> ([], (convertMethodDeclarationWithOptionals method), []) FieldDecls fields -> ([], [], fields) } | classbodydeclarations classbodydeclaration { case ($1, $2) of ((constructors, methods, fields), ConstructorDecl constructor) -> ((constructors ++ [constructor]), methods, fields) - ((constructors, methods, fields), MethodDecl method) -> (constructors, (methods ++ [method]), fields) + ((constructors, methods, fields), MethodDecl method) -> (constructors, (methods ++ (convertMethodDeclarationWithOptionals method)), fields) ((constructors, methods, fields), FieldDecls newFields) -> (constructors, methods, (fields ++ newFields)) } @@ -136,7 +137,7 @@ constructordeclaration : constructordeclarator constructorbody { case $1 of (ide fielddeclaration : type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $1) $2 } | modifiers type variabledeclarators SEMICOLON { FieldDecls $ map (convertDeclarator $2) $3 } -methoddeclaration : methodheader methodbody { case $1 of (returnType, (name, parameters)) -> MethodDecl (MethodDeclaration returnType name parameters $2) } +methoddeclaration : methodheader methodbody { case $1 of (returnType, (name, (parameters, optionalparameters))) -> MethodDecl (MethodDeclarationWithOptionals returnType name parameters optionalparameters $2) } block : LBRACKET RBRACKET { Block [] } | LBRACKET blockstatements RBRACKET { Block $2 } @@ -166,6 +167,10 @@ methodbody : block { $1 } blockstatements : blockstatement { $1 } | blockstatements blockstatement { $1 ++ $2} +formalandoptionalparameterlist : formalparameterlist { ($1, []) } + | formalparameterlist COMMA optionalparameterlist { ($1, $3) } + | optionalparameterlist { ([], $1) } + formalparameterlist : formalparameter { [$1] } | formalparameterlist COMMA formalparameter { $1 ++ [$3] } @@ -175,8 +180,13 @@ explicitconstructorinvocation : THIS LBRACE RBRACE SEMICOLON { } classtypelist : classtype { } | classtypelist COMMA classtype { } -methoddeclarator : IDENTIFIER LBRACE RBRACE { ($1, []) } - | IDENTIFIER LBRACE formalparameterlist RBRACE { ($1, $3) } +methoddeclarator : IDENTIFIER LBRACE RBRACE { ($1, ([], [])) } + | IDENTIFIER LBRACE formalandoptionalparameterlist RBRACE { ($1, $3) } + +optionalparameterlist : optionalparameter { [$1] } + | optionalparameterlist COMMA optionalparameter { $1 ++ [$3] } + +optionalparameter : type variabledeclaratorid ASSIGN variableinitializer { OptionalParameter $1 $2 $4 } primitivetype : BOOLEAN { "boolean" } | numerictype { $1 } @@ -386,9 +396,9 @@ multiplicativeexpression : unaryexpression { $1 } { -data MemberDeclaration = MethodDecl MethodDeclaration +data MemberDeclaration = MethodDecl MethodDeclarationWithOptionals | ConstructorDecl ConstructorDeclaration - | FieldDecls [VariableDeclaration] + | FieldDecls [VariableDeclaration] deriving (Show) data Declarator = Declarator Identifier (Maybe Expression) @@ -399,6 +409,22 @@ extractFunctionName :: Expression -> (Expression, Identifier) extractFunctionName (BinaryOperation NameResolution exp (Reference functionname)) = (exp, functionname) extractFunctionName (Reference functionname) = ((Reference "this"), functionname) +data OptionalParameter = OptionalParameter DataType Identifier Expression deriving (Show) +data MethodDeclarationWithOptionals = MethodDeclarationWithOptionals DataType Identifier [ParameterDeclaration] [OptionalParameter] Statement deriving (Show) +convertMethodDeclarationWithOptionals :: MethodDeclarationWithOptionals -> [MethodDeclaration] +convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType id param [] stmt) = [MethodDeclaration returnType id param stmt] +convertMethodDeclarationWithOptionals (MethodDeclarationWithOptionals returnType id param (opt : optRest) stmt) = generateHelperMethod returnType id param opt : convertMethodDeclarationWithOptionals (generateBaseMethod returnType id param opt optRest stmt) +convertOptionalParameter :: OptionalParameter -> ParameterDeclaration +convertOptionalParameter (OptionalParameter dtype id exp) = ParameterDeclaration dtype id +generateHelperMethod :: DataType -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> MethodDeclaration +generateHelperMethod returnType methodName params (OptionalParameter dtype id exp) = + let references = ((map (\(ParameterDeclaration paramType ident) -> (Reference ident)) params) ++ [exp]) + methodcall = (MethodCall (Reference "this") methodName references) + lastStatement = if returnType == "void" then StatementExpressionStatement methodcall else Return $ Just $ StatementExpressionExpression methodcall + in MethodDeclaration returnType methodName params $ Block [lastStatement] +generateBaseMethod :: DataType -> Identifier -> [ParameterDeclaration] -> OptionalParameter -> [OptionalParameter] -> Statement -> MethodDeclarationWithOptionals +generateBaseMethod returnType methodName params (OptionalParameter dtype id exp) optRest stmt = MethodDeclarationWithOptionals returnType methodName (params ++ [ParameterDeclaration dtype id]) optRest stmt + parseError :: ([Token], [String]) -> a parseError (errortoken, expected) = error ("parse error on token: " ++ show errortoken ++ "\nexpected one of: " ++ show expected)