From 3e04ad745c7c74a5098f5fa9c69b2616959df67d Mon Sep 17 00:00:00 2001 From: Kota Mizushima Date: Sat, 16 Nov 2024 23:47:42 +0900 Subject: [PATCH] Now you can use line terminator as statement terminator --- .gitignore | 8 + build.sbt | 11 + grammar/JJOnionParser.jj | 201 ++++++++++++------ run/Bean.on | 16 +- run/Calculator.on | 115 +++++----- src/main/scala/onion/tools/ScriptRunner.scala | 69 +++--- .../scala/onion/compiler/tools/BeanSpec.scala | 23 +- .../onion/compiler/tools/CountersSpec.scala | 18 +- .../onion/compiler/tools/ForeachSpec.scala | 6 +- .../FunctionWithExpressionBodySpec.scala | 2 +- .../onion/compiler/tools/HelloWorldSpec.scala | 2 +- .../onion/compiler/tools/ImportSpec.scala | 8 +- .../onion/compiler/tools/TerminatorSpec.scala | 42 ++++ 13 files changed, 331 insertions(+), 190 deletions(-) create mode 100644 src/test/scala/onion/compiler/tools/TerminatorSpec.scala diff --git a/.gitignore b/.gitignore index 999abf9..82631fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,11 @@ build/ .bsp/ build.lib/ + project/boot +project/metals.sbt +project/project + src/main/parser/onion target/ .idea/ @@ -10,3 +14,7 @@ target/ .build.sbt.un~ .settings + +.bloop/ +.metals/ +.vscode/ diff --git a/build.sbt b/build.sbt index 4ebc055..ac43730 100644 --- a/build.sbt +++ b/build.sbt @@ -11,6 +11,14 @@ lazy val dist = TaskKey[Unit]("onion-dist") lazy val distPath = SettingKey[File]("onion-dist-path") +lazy val runScript = inputKey[Unit]("Runs the ScriptRunner with arguments") + +fullRunInputTask( + runScript, + Compile, + "onion.tools.ScriptRunner" +) + def isArchive(file: File): Boolean = { val fileName = file.getName val lc = fileName.toLowerCase @@ -50,6 +58,7 @@ def javacc(classpath: Classpath, output: File, log: Logger): Seq[File] = { "javacc", "-UNICODE_INPUT=true", "-JAVA_UNICODE_ESCAPE=true", + "-BUILD_TOKEN_MANAGER=true", "-OUTPUT_DIRECTORY=%s/onion/compiler/parser".format(output.toString), "grammar/JJOnionParser.jj" ) @@ -117,3 +126,5 @@ lazy val onionSettings = Seq( mainClass := Some("onion.tools.CompilerFrontend"), assembly / assemblyJarName := "onion.jar" ) + +fork := true \ No newline at end of file diff --git a/grammar/JJOnionParser.jj b/grammar/JJOnionParser.jj index 71fb6eb..948294a 100644 --- a/grammar/JJOnionParser.jj +++ b/grammar/JJOnionParser.jj @@ -22,6 +22,7 @@ import scala.collection.mutable.Buffer; import scala.Option; import scala.Option$; import static onion.compiler.AST.append; +import java.util.Stack; @SuppressWarnings("unchecked") public class JJOnionParser implements Parser { @@ -126,26 +127,66 @@ public class JJOnionParser implements Parser { } return new String(b); } + + private Stack states = new Stack(); + + private void enterState(int state) { + int oldState = token_source.curLexState; + states.push(oldState); + token_source.SwitchTo(state); + } + + private void leaveState() { + token_source.SwitchTo(states.pop()); + } + + private void enterDefault() { + enterState(DEFAULT); + } + + private void leaveDefault() { + leaveState(); + } + + private void enterSection() { + enterState(IN_STATEMENT); + } + + private void leaveSection() { + leaveState(); + } } PARSER_END(JJOnionParser) -SKIP: + SKIP: { -" " + "\r\n" | "\t" -| "\n" +| "\f" | "\r" -| "\r\n" +| "\n" +| " " } + SKIP: +{ + " " +| "\t" +| "\f" +} + TOKEN: +{ + +} /* * COMMENTS */ + SPECIAL_TOKEN: { - + | | } @@ -153,6 +194,7 @@ SPECIAL_TOKEN: /* * KEYWORDS */ + TOKEN: { @@ -182,10 +224,11 @@ TOKEN: | | | -| +| | | | +| | | | @@ -215,6 +258,7 @@ TOKEN: /* * OPERATORS */ + TOKEN : { | @@ -269,6 +313,7 @@ TOKEN : { /* * LITERALS */ + TOKEN: { TOKEN:{ } @@ -334,7 +380,8 @@ AST.CompilationUnit unit() :{ } AST.ModuleDeclaration module_decl() :{Token t1, t2; StringBuffer sb = new StringBuffer();}{ - t1="module" t2= {sb.append(t2.image);} ("." t2= {sb.append(t2.image);})* ";" { + {enterSection();} + t1="module" t2= {sb.append(t2.image);} ("." t2= {sb.append(t2.image);})* eos() { return new AST.ModuleDeclaration(p(t1), new String(sb)); } } @@ -346,10 +393,10 @@ AST.ImportClause import_decl() :{ ArrayBuffer> imports = new ArrayBuffer>(); }{ t="import" "{" - ( {sb = new StringBuffer();} + ( {sb = new StringBuffer();enterSection();} ( LOOKAHEAD(2) - (n= {s = n.image;} "=" n= {sb.append(n.image);} ("." n= {sb.append("."); sb.append(n.image);})+ ";") - | ((LOOKAHEAD(2) n= "." {sb.append(n.image); sb.append(".");})+ (n= | n="*") ";" { + (n= {s = n.image;} "=" n= {sb.append(n.image);} ("." n= {sb.append("."); sb.append(n.image);})+ eos()) + | ((LOOKAHEAD(2) n= "." {sb.append(n.image); sb.append(".");})+ (n= | n="*") eos() { s = n.image; sb.append(s); } ) @@ -374,7 +421,8 @@ AST.GlobalVariableDeclaration var_decl(int modifiers) : { AST.TypeNode ty = null; AST.Expression e = null; }{ - t1="var" t2= [":" ty=type()] ["=" e=term()] ";" { + {enterSection();} + t1="var" t2= [":" ty=type()] ["=" e=term()] eos() { return new AST.GlobalVariableDeclaration( p(t1), modifiers, t2.image.substring(1), ty, e ); @@ -388,11 +436,12 @@ AST.FunctionDeclaration fun_decl(int modifiers) : { AST.BlockExpression b = null; AST.Expression e = null; }{ + {enterSection();} t1="def" t2= ["(" args=args() ")"] [":" ty=return_type()] - ( (b=block() | ";") { + ( (b=block() {leaveSection();} | eos()) { return new AST.FunctionDeclaration(p(t1), modifiers, t2.image, args, ty, b); } - | ("=" e=term() ";") { + | ("=" eols() e=term() eos()) { return new AST.FunctionDeclaration( p(t1), modifiers, t1.image, args, ty, new AST.BlockExpression(p(t1), asList(new AST.ReturnExpression(p(t1), e))) @@ -522,7 +571,7 @@ AST.ConstructorDeclaration constructor_decl(int mset) : { List params = (List)AST.NIL(); AST.BlockExpression block; }{ - "def" t = "new" ["(" args = args() ")"] [":" "(" params = terms() ")"] block = block() { + "def" t="this" ["(" args = args() ")"] [":" "(" params = terms() ")"] block = block() { return new AST.ConstructorDeclaration(p(t), mset, args, params, block); } } @@ -535,10 +584,10 @@ AST.MethodDeclaration method_decl(int mset) : { AST.Expression e = null; }{ "def" t= ["(" args=args() ")"] [":" ty=return_type()] - ( (b=block() | ";" ) { + ( (b=block() | {enterSection();} eos() ) { return new AST.MethodDeclaration(p(t), mset, t.image, args, ty, b); } - | ("=" e=term() ";") { + | ({enterSection();} "=" eols() e=term() eos()) { return new AST.MethodDeclaration( p(t), mset, t.image, args, ty, new AST.BlockExpression(p(t), asList(new AST.ReturnExpression(p(t), e))) @@ -552,7 +601,8 @@ AST.MethodDeclaration interface_method_decl() : { List args = (List)AST.NIL(); AST.TypeNode ty = null; }{ - "def" n= ["(" args=args() ")"] [":" ty=return_type()] ";" { + {enterSection();} + "def" n= ["(" args=args() ")"] [":" ty=return_type()] eos() { return new AST.MethodDeclaration(p(n), AST.M_PUBLIC(), n.image, args, ty, null); } } @@ -618,8 +668,9 @@ AST.DelegatedFieldDeclaration delegate_decl(int modifiers) : { AST.TypeNode type = null; AST.Expression init = null; }{ + {enterSection();} start="forward" name= [":" type = type()] - ["=" init=term()] ";" { + ["=" eols() init=term()] eos() { return new AST.DelegatedFieldDeclaration(p(start), modifiers, name.image.substring(1), type, init); } } @@ -629,8 +680,9 @@ AST.FieldDeclaration field_decl(int modifiers):{ AST.TypeNode type = null; AST.Expression init = null; }{ + {enterSection();} name= ":" [type=type()] - ["=" init=term()] ";" { + ["=" eols() init=term()] eos() { return new AST.FieldDeclaration(p(name), modifiers, name.image.substring(1), type, init); } } @@ -654,20 +706,34 @@ AST.CompoundExpression statement():{AST.CompoundExpression s;}{ | s=foreach_statement() {return s;} } +// End of section +void eos() :{} { + (";" | ()) { leaveSection(); } +} + +void eols() :{} { + ()* +} + AST.LocalVariableDeclaration local_var_statement() :{Token t; AST.TypeNode ty; AST.Expression e = null;}{ - t= ":" ty=type() ["=" e=term()] ";" {return new AST.LocalVariableDeclaration(p(t), t.image, ty, e);} + {enterSection();} + t= + ":" ty=type() ["=" eols() e=term()] eos() {return new AST.LocalVariableDeclaration(p(t), t.image, ty, e);} } AST.BreakExpression break_statement() :{Token t;}{ - t="break" ";" {return new AST.BreakExpression(p(t));} + {enterSection();} + t="break" eos() {return new AST.BreakExpression(p(t));} } AST.ContinueExpression continue_statement() :{Token t; AST.ContinueExpression st;}{ - t="continue" ";" {return new AST.ContinueExpression(p(t));} + {enterSection();} + t="continue" eos() {return new AST.ContinueExpression(p(t));} } AST.ThrowExpression throw_statement() :{Token t; AST.Expression e;}{ - t="throw" e=term() ";" {return new AST.ThrowExpression(p(t), e);} + {enterSection();} + t="throw" e=term() eos() {return new AST.ThrowExpression(p(t), e);} } AST.TryExpression try_statement() :{ @@ -679,7 +745,8 @@ AST.TryExpression try_statement() :{ } AST.ExpressionBox exp_statement() :{AST.Expression e;}{ - e=term() ";" {return new AST.ExpressionBox(e.location(), e);} + {enterSection();} + e=term() eos() {return new AST.ExpressionBox(e.location(), e);} } AST.EmptyExpression empty_statement() :{Token t;}{ @@ -708,7 +775,8 @@ AST.SelectExpression select_statement() :{ } AST.ReturnExpression return_statement() :{Token t; AST.Expression e = null;}{ - t="return" [e=term()] ";" {return new AST.ReturnExpression(p(t), e);} + {enterSection();} + t="return" [e=term()] eos() {return new AST.ReturnExpression(p(t), e);} } AST.SynchronizedExpression synchronized_statement() :{Token t; AST.Expression e = null; AST.BlockExpression b;}{ @@ -738,72 +806,72 @@ AST.Expression term() :{AST.Expression e;}{ AST.Expression assignable() :{Token t; AST.Expression a, b; }{ a=logical_or() - [ ( t="=" b=assignable() {a = new AST.Assignment(p(t), a, b);} - | t="+=" b=assignable() {a = new AST.AdditionAssignment(p(t), a, b);} - | t="-=" b=assignable() {a = new AST.SubtractionAssignment(p(t), a, b);} - | t="*=" b=assignable() {a = new AST.MultiplicationAssignment(p(t), a, b);} - | t="/=" b=assignable() {a = new AST.DivisionAssignment(p(t), a, b);} - | t="%=" b=assignable() {a = new AST.ModuloAssignment(p(t), a, b);} + [ ( t="=" eols() b=assignable() {a = new AST.Assignment(p(t), a, b);} + | t="+=" eols() b=assignable() {a = new AST.AdditionAssignment(p(t), a, b);} + | t="-=" eols() b=assignable() {a = new AST.SubtractionAssignment(p(t), a, b);} + | t="*=" eols() b=assignable() {a = new AST.MultiplicationAssignment(p(t), a, b);} + | t="/=" eols() b=assignable() {a = new AST.DivisionAssignment(p(t), a, b);} + | t="%=" eols() b=assignable() {a = new AST.ModuloAssignment(p(t), a, b);} ) ]{return a;} } AST.Expression logical_or() :{Token t; AST.Expression a, b; }{ a=logical_and() - ( t="||" b=logical_and() {a = new AST.LogicalOr(p(t), a, b);} - | t="?:" b=logical_and() {a = new AST.Elvis(p(t), a, b);})* + ( t="||" eols() b=logical_and() {a = new AST.LogicalOr(p(t), a, b);} + | t="?:" eols() b=logical_and() {a = new AST.Elvis(p(t), a, b);})* { return a; } } AST.Expression logical_and() :{Token t; AST.Expression a, b; }{ - a=bit_or() (t="&&" b=bit_or() {a = new AST.LogicalAnd(p(t), a, b);})* {return a;} + a=bit_or() (t="&&" eols() b=bit_or() {a = new AST.LogicalAnd(p(t), a, b);})* {return a;} } AST.Expression bit_or() :{Token t; AST.Expression a, b; }{ - a=xor() (t="|" b=xor() {a = new AST.BitOr(p(t), a, b);})* {return a;} + a=xor() (t="|" eols() b=xor() {a = new AST.BitOr(p(t), a, b);})* {return a;} } AST.Expression xor() :{Token t; AST.Expression a, b;}{ - a = bit_and() (t="^" b=bit_and() {a = new AST.XOR(p(t), a, b);})* {return a;} + a = bit_and() (t="^" eols() b=bit_and() {a = new AST.XOR(p(t), a, b);})* {return a;} } AST.Expression bit_and() :{Token t; AST.Expression a, b; }{ - a=equal() (t = "&" b=equal() {a = new AST.BitAnd(p(t), a, b);})* {return a;} + a=equal() (t = "&" eols() b=equal() {a = new AST.BitAnd(p(t), a, b);})* {return a;} } AST.Expression equal() :{ Token t; AST.Expression a, b; }{ a=comparative() - ( ( t="===" b=comparative() {a= new AST.ReferenceEqual(p(t), a, b);} - | t="!==" b=comparative() {a= new AST.ReferenceNotEqual(p(t), a, b);} - | t="==" b=comparative() {a= new AST.Equal(p(t), a, b);} - | t="!=" b=comparative() {a= new AST.NotEqual(p(t), a, b);} + ( ( t="===" eols() b=comparative() {a= new AST.ReferenceEqual(p(t), a, b);} + | t="!==" eols() b=comparative() {a= new AST.ReferenceNotEqual(p(t), a, b);} + | t="==" eols() b=comparative() {a= new AST.Equal(p(t), a, b);} + | t="!=" eols() b=comparative() {a= new AST.NotEqual(p(t), a, b);} ) )* {return a;} } AST.Expression comparative() : { Token t; AST.Expression a, b; AST.TypeNode type; }{ a=bit_shift() - ( ( t="<=" b=bit_shift() {a = new AST.LessOrEqual(p(t), a, b);} - | t=">=" b=bit_shift() {a = new AST.GreaterOrEqual(p(t), a, b);} - | t="<" b=bit_shift() {a = new AST.LessThan(p(t), a, b);} - | t=">" b=bit_shift() {a = new AST.GreaterThan(p(t), a, b);} - | t="is" type = type() {a = new AST.IsInstance(p(t), a, type);} + ( ( t="<=" eols() b=bit_shift() {a = new AST.LessOrEqual(p(t), a, b);} + | t=">=" eols() b=bit_shift() {a = new AST.GreaterOrEqual(p(t), a, b);} + | t="<" eols() b=bit_shift() {a = new AST.LessThan(p(t), a, b);} + | t=">" eols() b=bit_shift() {a = new AST.GreaterThan(p(t), a, b);} + | t="is" eols() type = type() {a = new AST.IsInstance(p(t), a, type);} ) )* {return a;} } AST.Expression bit_shift() :{Token t; AST.Expression e1, e2;}{ e1=additive() -( t="<<" e2=additive() {e1 = new AST.MathLeftShift(p(t), e1, e2);} -| t=">>" e2=additive() {e1 = new AST.MathRightShift(p(t), e1, e2);} -| t=">>>"e2=additive() {e1 = new AST.LogicalRightShift(p(t), e1, e2);} +( t="<<" eols() e2=additive() {e1 = new AST.MathLeftShift(p(t), e1, e2);} +| t=">>" eols() e2=additive() {e1 = new AST.MathRightShift(p(t), e1, e2);} +| t=">>>"eols() e2=additive() {e1 = new AST.LogicalRightShift(p(t), e1, e2);} )* { return e1; } } AST.Expression additive() :{Token t; AST.Expression e1, e2;}{ e1=unary_prefix() -( t="+" e2=unary_prefix() {e1 = new AST.Addition(p(t), e1, e2);} -| t="-" e2=unary_prefix() {e1 = new AST.Subtraction(p(t), e1, e2);} +( t="+" eols() e2=unary_prefix() {e1 = new AST.Addition(p(t), e1, e2);} +| t="-" eols() e2=unary_prefix() {e1 = new AST.Subtraction(p(t), e1, e2);} )* {return e1;} } @@ -817,9 +885,9 @@ AST.Expression unary_prefix() :{Token t; AST.Expression e;}{ AST.Expression multitive() :{Token t; AST.Expression e1, e2;}{ e1=primary_suffix() -( t="*" e2=primary_suffix() {e1 = new AST.Multiplication(p(t), e1, e2);} -| t="/" e2=primary_suffix() {e1 = new AST.Division(p(t), e1, e2);} -| t="%" e2=primary_suffix() {e1 = new AST.Modulo(p(t), e1, e2);} +( t="*" eols() e2=primary_suffix() {e1 = new AST.Multiplication(p(t), e1, e2);} +| t="/" eols() e2=primary_suffix() {e1 = new AST.Division(p(t), e1, e2);} +| t="%" eols() e2=primary_suffix() {e1 = new AST.Modulo(p(t), e1, e2);} )* {return e1;} } @@ -828,12 +896,11 @@ AST.Expression primary_suffix() : { AST.TypeNode type; }{ e=primary() -( t="[" a=term() "]" {e = new AST.Indexing(p(t), e, a);} -| LOOKAHEAD(3) t="." n= "(" args=terms() ")" [a=anonymous_function()] {e = new AST.MethodCall(p(t), e, c(n), a != null ? args.reverse().$colon$colon(a).reverse() : args);} -| t="." n= {e = new AST.MemberSelection(p(t), e, c(n));} -| t="$" type=type() {e = new AST.Cast(p(t), e, type);} -| t="++" {e = new AST.PostIncrement(p(t), e);} -| t="--" {e = new AST.PostDecrement(p(t), e);} +( t="[" eols() a=term() eols() "]" {e = new AST.Indexing(p(t), e, a);} +| t="." eols() n= (LOOKAHEAD(1) "(" eols() args=terms() ")" [a=anonymous_function()] {e = new AST.MethodCall(p(t), e, c(n), a != null ? args.reverse().$colon$colon(a).reverse() : args);} | {e = new AST.MemberSelection(p(t), e, c(n));}) +| t="$" eols() type=type() {e = new AST.Cast(p(t), e, type);} +| t="++" {e = new AST.PostIncrement(p(t), e);} +| t="--" {e = new AST.PostDecrement(p(t), e);} )* {return e;} } @@ -844,16 +911,16 @@ AST.Expression primary() : { AST.BlockExpression body; }{ t= {return new AST.UnqualifiedFieldReference(p(t), c(t).substring(1));} -| t="super" "." n= ["(" es=terms() ")"] {return new AST.SuperMethodCall(p(t), c(n), es);} +| t="super" eols() "." eols() n= ["(" es=terms() ")"] {return new AST.SuperMethodCall(p(t), c(n), es);} | LOOKAHEAD(4) ty=class_type() t="::" n= "(" es=terms() ")" {return new AST.StaticMethodCall(p(t), ty, c(n), es);} | LOOKAHEAD(2) ty=class_type() t="::" n= {return new AST.StaticMemberSelection(p(t), ty, c(n));} | LOOKAHEAD(2) t= "(" es=terms() ")" {return new AST.UnqualifiedMethodCall(p(t), c(t), es);} | t= {return new AST.Id(p(t), c(t));} -| t="[" es=terms() "]" {return new AST.ListLiteral(p(t), es);} +| {enterDefault();} t="[" eols() es=terms() "]" {leaveDefault();} {return new AST.ListLiteral(p(t), es);} | e=anonymous_function() {return e;} | t="new" ty=type() ( - "[" (es=terms()) "]" {return new AST.NewArray(p(t), ty, es);} - | ["(" (es=terms()) ")"] {return new AST.NewObject(p(t), ty, es);} + "[" eols() (es=terms()) "]" {return new AST.NewArray(p(t), ty, es);} + | ["(" eols() (es=terms()) ")"] {return new AST.NewObject(p(t), ty, es);} ) | t="self" {return new AST.CurrentInstance(p(t));} | e=integer_literal() {return e;} @@ -862,7 +929,7 @@ AST.Expression primary() : { | e=string_literal() {return e;} | e=boolean_literal() {return e;} | e=null_literal() {return e;} -| "(" e=term() ")" {return e;} +| "(" eols() e=term() eols() ")" {return e;} } List terms() :{AST.Expression arg; ArrayBuffer args = new ArrayBuffer();}{ @@ -876,7 +943,8 @@ AST.ClosureExpression anonymous_function() :{ List args = (List)AST.NIL(); AST.BlockExpression body; }{ - t="#" [ ty=class_type() "." n=] ["(" args=args() ")"] body=block() { + {enterDefault();} + t="#" [ ty=class_type() "." n=] ["(" args=args() ")"] body=block() { String mname; if(ty == null) { ty = new AST.TypeNode(p(t), new AST.ReferenceType("onion.Function" + args.size(), true), true); @@ -884,6 +952,7 @@ AST.ClosureExpression anonymous_function() :{ }else { mname = n.image; } + leaveDefault(); return new AST.ClosureExpression(p(t), ty, mname, args, null, body); } } diff --git a/run/Bean.on b/run/Bean.on index 8239d56..1a7fc6c 100644 --- a/run/Bean.on +++ b/run/Bean.on @@ -8,32 +8,32 @@ class ExampleBean <: Serializable { @value :Int; public: - def new { + def this { } - def new(name :String, value :Int){ + def this(name :String, value :Int){ @name = name; @value = value; } def getName :String { - return @name; + return @name; } def setName(name :String) { - @name = name; + @name = name; } def setValue(value :Int) { - @value = value; + @value = value; } def getValue :Int { - return @value; + return @value; } def toString :String { - return "ExampleBean(name = " + @name + ", value = " + @value + ")"; + return "ExampleBean(name = " + @name + ", value = " + @value + ")"; } } @@ -45,4 +45,4 @@ encoder.close; decoder = new XMLDecoder(new ByteArrayInputStream(out.toByteArray())); bean = decoder.readObject$ExampleBean; -System::out.println(bean); \ No newline at end of file +System::out.println(bean); diff --git a/run/Calculator.on b/run/Calculator.on index 2d54f02..ad599e6 100644 --- a/run/Calculator.on +++ b/run/Calculator.on @@ -1,99 +1,98 @@ -import{ - javax.swing.*; - java.awt.*; - java.awt.event.*; +import { + javax.swing.* + java.awt.* + java.awt.event.* } class Calculator : JFrame <: ActionListener { - @text :JTextField; - @left :Long; - @right :Long; - @operator :String; - @isError :Boolean; + @text :JTextField + @left :Long + @right :Long + @operator :String + @isError :Boolean public: def setValue(value : Long) { @text.setText(JLong::toString(value)); } def setValue(value : String) { @text.setText(value); } def actionPerformed(event : ActionEvent) { - label = event.source$JButton.label; + label = event.source$JButton.label try{ if @isError && !(label == "C") { - return; + return } - input = JLong::parseLong(label); + input = JLong::parseLong(label) if @operator != null { - @right = @right * 10 + input; - setValue(@right); + @right = @right * 10 + input + setValue(@right) }else{ - @left = @left * 10 + input; - setValue(@left); + @left = @left * 10 + input + setValue(@left) } }catch e :NumberFormatException{ if label == "C" { - @left = 0; - @right = 0; - @operator = null; - @text.setText(JLong::toString(0)); - @isError = false; + @left = 0 + @right = 0 + @operator = null + @text.setText(JLong::toString(0)) + @isError = false }else{ if @operator != null { if @operator == "/" { if @right == 0L{ - setValue("0で割ることはできません。"); - @left = 0L; - @right = 0L; - @isError = true; + setValue("0で割ることはできません。") + @left = 0L + @right = 0L + @isError = true }else{ - @left = @left / @right; + @left = @left / @right } - setValue(@left); + setValue(@left) }else{ select @operator { - case "+": @left = @left + @right; - case "-": @left = @left - @right; - case "*": @left = @left * @right; - case "/": @left = @left / @right; + case "+": @left = @left + @right + case "-": @left = @left - @right + case "*": @left = @left * @right + case "/": @left = @left / @right } - setValue(@left); + setValue(@left) } } - @operator = label; - @right = 0; + @operator = label + @right = 0 } } } - def new:("簡易電卓") { - setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE); - setSize(800, 600); - @text = new JTextField; - @text.setHorizontalAlignment(JTextField::RIGHT); - pane = getContentPane(); - north = new JPanel; - north.setLayout(new BorderLayout); - north.add(@text, BorderLayout::NORTH); - center = new JPanel; - center.setLayout(new GridLayout(4, 5, 4, 3)); - center.setFont(new Font(null, Font::PLAIN, 8)); - labels = [ + def this:("簡易電卓") { + setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE) + setSize(800, 600) + @text = new JTextField + @text.setHorizontalAlignment(JTextField::RIGHT) + pane = getContentPane() + north = new JPanel + north.setLayout(new BorderLayout) + north.add(@text, BorderLayout::NORTH) + center= new JPanel + center.setLayout(new GridLayout(4, 5, 4, 3)) + center.setFont(new Font(null, Font::PLAIN, 8)) + labels = [ "7", "8", "9", "C", "4", "5", "6", "*", "1", "2", "3", "-", "0", "=", "/", "+" - ]; + ] foreach label:String in labels { - button = new JButton(label$String); - button.addActionListener(self); - button.setPreferredSize(new Dimension(42, 28)); - center.add(button); + button = new JButton(label$String) + button.addActionListener(self) + button.setPreferredSize(new Dimension(42, 28)) + center.add(button) } - pane.add(north, BorderLayout::NORTH); - pane.add(center, BorderLayout::CENTER); + pane.add(north, BorderLayout::NORTH) + pane.add(center, BorderLayout::CENTER) } static def main(args :String[]){ - UIManager::setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); - frame = new Calculator; - frame.pack; - frame.setVisible(true); + frame = new Calculator + frame.pack + frame.setVisible(true) } } diff --git a/src/main/scala/onion/tools/ScriptRunner.scala b/src/main/scala/onion/tools/ScriptRunner.scala index aca4b8c..d19f7d1 100644 --- a/src/main/scala/onion/tools/ScriptRunner.scala +++ b/src/main/scala/onion/tools/ScriptRunner.scala @@ -63,7 +63,7 @@ class ScriptRunner { val config = createConfig(success) if (config.isEmpty) return -1 createConfig(success) match { - case None => return - 1 + case None => return -1 case Some (config) => val params = success.arguments if(params.length == 0) { @@ -101,13 +101,10 @@ class ScriptRunner { private def createConfig(result: ParseSuccess): Option[CompilerConfig] = { val option: Map[String, CommandLineParam] = result.options.toMap - val classpath: Array[String] = checkClasspath(option.get(CLASSPATH).asInstanceOf[String]) - val encodingOpt = checkEncoding(option.get(ENCODING).asInstanceOf[String]) - - val maxErrorReport = checkMaxErrorReport(option.get(MAX_ERROR).asInstanceOf[String]) + val classpath: Array[String] = checkClasspath(option.get(CLASSPATH)) for( - encoding <- checkEncoding(option.get(ENCODING).asInstanceOf[String]); - maxErrorReport <- checkMaxErrorReport(option.get(MAX_ERROR).asInstanceOf[String]) + encoding <- checkEncoding(option.get(ENCODING)); + maxErrorReport <- checkMaxErrorReport(option.get(MAX_ERROR)) ) yield (new CompilerConfig(classpath.toIndexedSeq, "", encoding, ".", maxErrorReport)) } @@ -115,36 +112,44 @@ class ScriptRunner { new OnionCompiler(config).compile(fileNames) } - private def checkClasspath(classpath: String): Array[String] = { - if (classpath == null) return DEFAULT_CLASSPATH - val paths: Array[String] = pathArray(classpath) - paths + private def checkClasspath(optClasspath: Option[CommandLineParam]): Array[String] = { + optClasspath match { + case Some(ValuedParam(classpath)) => pathArray(classpath) + case Some(NoValuedParam) | None => DEFAULT_CLASSPATH + } } - private def checkEncoding(encoding: String): Option[String] = { - if (encoding == null) return Some(System.getProperty("file.encoding")) - try { - "".getBytes(encoding) - Some(encoding) - } catch { - case e: UnsupportedEncodingException => - err.println(Message.apply("error.command.invalidEncoding", ENCODING)) - None + private def checkEncoding(optEncoding: Option[CommandLineParam]): Option[String] = { + optEncoding match { + case None | Some(NoValuedParam) => + Some(System.getProperty("file.encoding")) + case Some(ValuedParam(encoding)) => + try { + "".getBytes(encoding) + Some(encoding) + } catch { + case e: UnsupportedEncodingException => + err.println(Message.apply("error.command.invalidEncoding", ENCODING)) + None + } } } - private def checkMaxErrorReport(maxErrorReport: String): Option[Int] = { - if (maxErrorReport == null) return Some(DEFAULT_MAX_ERROR) - val value: Option[Int] = try { - Some(Integer.parseInt(maxErrorReport)) - } catch { - case e: NumberFormatException => None - } - value match { - case Some(v) if v > 0 => Some(v) - case None => - err.println(Message("error.command.requireNaturalNumber", MAX_ERROR)) - None + private def checkMaxErrorReport(optMaxErrorReport: Option[CommandLineParam]): Option[Int] = { + optMaxErrorReport match { + case None | Some(NoValuedParam) => Some(DEFAULT_MAX_ERROR) + case Some(ValuedParam(maxErrorReport)) => + val value: Option[Int] = try { + Some(Integer.parseInt(maxErrorReport)) + } catch { + case e: NumberFormatException => None + } + value match { + case Some(v) if v > 0 => Some(v) + case None => + err.println(Message("error.command.requireNaturalNumber", MAX_ERROR)) + None + } } } } diff --git a/src/test/scala/onion/compiler/tools/BeanSpec.scala b/src/test/scala/onion/compiler/tools/BeanSpec.scala index e683578..c5543a2 100644 --- a/src/test/scala/onion/compiler/tools/BeanSpec.scala +++ b/src/test/scala/onion/compiler/tools/BeanSpec.scala @@ -8,24 +8,27 @@ class BeanSpec extends AbstractShellSpec { val resultBean = shell.run( """ | class Bean <: Serializable { - | @value :Int; + | @value :Int | public: - | def new { + | def this { | } | - | def new(value :Int){ - | @value = value; + | def this(value :Int){ + | @value = value | } - | def getValue :Int = @value; + | + | def getValue :Int = @value + | | def setValue(value :Int) { - | @value = value; + | @value = value | } - | def toString :String = "Bean(value = " + @value + ")"; + | + | def toString :String = "Bean(value = " + @value + ")" | | static def main(args: String[]): String { - | bean = new Bean; - | bean.value = 200; // The reference of setter functions! - | return JInteger::toString(bean.value); + | bean = new Bean + | bean.value = 200 // The reference of setter functions! + | return JInteger::toString(bean.value) | } | } """.stripMargin, diff --git a/src/test/scala/onion/compiler/tools/CountersSpec.scala b/src/test/scala/onion/compiler/tools/CountersSpec.scala index 25133c5..e6de918 100644 --- a/src/test/scala/onion/compiler/tools/CountersSpec.scala +++ b/src/test/scala/onion/compiler/tools/CountersSpec.scala @@ -8,18 +8,22 @@ class CountersSpec extends AbstractShellSpec { val result = shell.run( """ | interface Counter { - | def count :Int; + | def count :Int | } | | class Counters { | public: - | static def counter(begin :Int, up :Int) :Counter = - | #Counter.count { return begin = begin + up; }; + | static def counter(begin :Int, up :Int) :Counter = + | #Counter.count { + | return begin = + | begin + up + | } + | | static def main(args: String[]): Int { - | c = counter(1, 10); - | c.count; - | c.count; - | return c.count; + | c = counter(1, 10) + | c.count + | c.count + | return c.count | } |} |""".stripMargin, diff --git a/src/test/scala/onion/compiler/tools/ForeachSpec.scala b/src/test/scala/onion/compiler/tools/ForeachSpec.scala index 0a26793..541b1fc 100644 --- a/src/test/scala/onion/compiler/tools/ForeachSpec.scala +++ b/src/test/scala/onion/compiler/tools/ForeachSpec.scala @@ -10,12 +10,12 @@ class ForeachSpec extends AbstractShellSpec { |class Cat { |public: | static def main(args: String[]): String { - | list = ["A", "B", "C", "D"]; - | result = ""; + | list = ["A", "B", "C", "D"] + | result = "" | foreach s:String in list { | result = result + s; | } - | return result; + | return result | } |} """.stripMargin, diff --git a/src/test/scala/onion/compiler/tools/FunctionWithExpressionBodySpec.scala b/src/test/scala/onion/compiler/tools/FunctionWithExpressionBodySpec.scala index d01a17b..b3acdcb 100644 --- a/src/test/scala/onion/compiler/tools/FunctionWithExpressionBodySpec.scala +++ b/src/test/scala/onion/compiler/tools/FunctionWithExpressionBodySpec.scala @@ -9,7 +9,7 @@ class FunctionWithExpressionBodySpec extends AbstractShellSpec { """ |class ExpressionBody { |public: - | static def main(args: String[]): String = "ExpressionBody"; + | static def main(args: String[]): String = "ExpressionBody" |} """.stripMargin, "None", diff --git a/src/test/scala/onion/compiler/tools/HelloWorldSpec.scala b/src/test/scala/onion/compiler/tools/HelloWorldSpec.scala index 2d7c91d..86b83a5 100644 --- a/src/test/scala/onion/compiler/tools/HelloWorldSpec.scala +++ b/src/test/scala/onion/compiler/tools/HelloWorldSpec.scala @@ -11,7 +11,7 @@ class HelloWorldSpec extends AbstractShellSpec { |class HelloWorld { |public: | static def main(args: String[]): String { - | return "Hello, World"; + | return "Hello, World" | } |} |""".stripMargin, diff --git a/src/test/scala/onion/compiler/tools/ImportSpec.scala b/src/test/scala/onion/compiler/tools/ImportSpec.scala index 2f35700..156e4af 100644 --- a/src/test/scala/onion/compiler/tools/ImportSpec.scala +++ b/src/test/scala/onion/compiler/tools/ImportSpec.scala @@ -8,14 +8,14 @@ class ImportSpec extends AbstractShellSpec { val result = shell.run( """ | import { - | java.util.*; + | java.util.* | } | class Increment { | public: | static def main(args: String[]): Int { - | xs = new ArrayList(); - | xs.add(new Integer(2)); - | return xs.get(0)$Integer.intValue(); + | xs = new ArrayList() + | xs.add(new Integer(2)) + | return xs.get(0)$Integer.intValue() | } | } """.stripMargin, diff --git a/src/test/scala/onion/compiler/tools/TerminatorSpec.scala b/src/test/scala/onion/compiler/tools/TerminatorSpec.scala new file mode 100644 index 0000000..7a3e60e --- /dev/null +++ b/src/test/scala/onion/compiler/tools/TerminatorSpec.scala @@ -0,0 +1,42 @@ +package onion.compiler.tools + +import onion.tools.Shell + +class TerminatorSpec extends AbstractShellSpec { + describe("Newlines as statement terminators") { + it("local variable declaration") { + val result = shell.run( + """ + | class LocalVar { + | public: + | static def main(args: String[]): Int { + | i: Int = 10 + | j: Int = + | 20 + | return 0; + | } + | } + """.stripMargin, + "None", + Array() + ) + assert(Shell.Success(0) == result) + } + + it("return statement") { + val result = shell.run( + """ + | class ReturnStatement { + | public: + | static def main(args: String[]): Int { + | return 20 + | } + | } + """.stripMargin, + "None", + Array() + ) + assert(Shell.Success(20) == result) + } + } +} \ No newline at end of file