diff --git a/src/main/rascal/AST.rsc b/src/main/rascal/AST.rsc index e69de29..dacc53b 100644 --- a/src/main/rascal/AST.rsc +++ b/src/main/rascal/AST.rsc @@ -0,0 +1,87 @@ +module AST + +data AProgram(loc src = |tmp:///|) + = program(AId id, list[AConst] consts, list[AVar] vars, list[ASubProg] subProgs, ABlock main) + ; + +data AConst(loc src = |tmp:///|) + = const(AId id, AType \type, AValue \value) + ; +data AVar(loc src = |tmp:///|) + = var(AId id, AType \type) + ; + +data ASubProg(loc src = |tmp:///|) + = function(AId id, list[AParam] parameters, AType \returnType, list[AVar] locals, ABlock body) + | procedure(AId id, list[AParam] parameters, list[AVar] locals, ABlock body) + ; + +data AParam(loc src = |tmp:///|) + = referenceParam(AId id, AType \type) + | valueParam(AId id, AType \type) + ; + +data AType(loc src = |tmp:///|) + = simpleType(ABasicType \basic) + | arrayType(ABasicType \basic) + ; +data ABasicType(loc src = |tmp:///|) + = integerType() + | realType() + ; + +data ABlock(loc src = |tmp:///|) + = block(list[AStatement] body) + ; +data AStatement(loc src = |tmp:///|) + = assignment(ALhs lhs, AExpr res) + | call(ASubProgCall subProg) + | skip() + | blockStatement(ABlock block) + | \if(AGuard guard, AStatement ifBody, AStatement elseBody) + | \while(AGuard guard, AStatement body) + ; + +data ASubProgCall(loc src = |tmp:///|) + = call(AId id, list[AExpr] args) + | readln(list[AId] ids) + | writeln(list[AExpr] args) + ; + +data AGuard(loc src = |tmp:///|) + = lessThan(AExpr left, AExpr right) + | lessThanEqual(AExpr left, AExpr right) + | greaterThan(AExpr left, AExpr right) + | greaterThanEqual(AExpr left, AExpr right) + | equal(AExpr left, AExpr right) + | not(AGuard inner) + | and(AGuard left, AGuard right) + | or(AGuard left, AGuard right) + ; + +data ALhs(loc src = |tmp:///|) + = simple(AId id) + | indexed(AId id, AExpr index) + ; + +data AExpr(loc src = |tmp:///|) + = ref(AId id) + | index(AId id, AExpr index) + | slice(AId id, AExpr \start, AExpr end) + | val(AValue val) + | call(list[AExpr] args) + | add(AExpr left, AExpr right) + | sub(AExpr left, AExpr right) + | mul(AExpr left, AExpr right) + | div(AExpr left, AExpr right) + | \mod(AExpr left, AExpr right) + | intDiv(AExpr left, AExpr right) + | negate(AExpr inner) + ; + +data AValue(loc src = |tmp:///|) + = integer(int n) + | \real(real n) + ; +data AId(loc src = |tmp:///|) + = id(str name); \ No newline at end of file diff --git a/src/main/rascal/CST2AST.rsc b/src/main/rascal/CST2AST.rsc index e69de29..a9490a7 100644 --- a/src/main/rascal/CST2AST.rsc +++ b/src/main/rascal/CST2AST.rsc @@ -0,0 +1,118 @@ +module CST2AST + +import Syntax; +import AST; +import String; +import Exception; + +// TODO: Add source + +// -------------------------------- Top level program ----------------------- + +AProgram cst2ast(start[Program] program) { + // Remove layout + Program p = program.top; + return cst2ast(p); +} + +AProgram cst2ast(Program p) + = program( + cst2ast(p.name), + cst2ast(p.consts), + cst2ast(p.vars), + cst2ast(p.subProgs), + cst2ast(p.main) + ); + +AId cst2ast(Id name) + = id(""); + +// -------------------------------------- Declarations ----------------------- + +list[AConst] cst2ast(ConstDecl* consts) + = [cst2ast(const) | const <- consts]; + +AConst cst2ast(ConstDecl cDecl) + = const(cst2ast(cDecl.id), inferType(cDecl.\value), cst2ast(cDecl.\value)); + +list[AVar] cst2ast(VarDecl* varDecls) + = [aVar | varDecl <- varDecls, aVar <- cst2ast(varDecl)]; +list[AVar] cst2ast(VarDecl varDecl) + = [var(cst2ast(id), cst2ast(varDecl.\type)) | id <- varDecl.ids]; + +list[ASubProg] cst2ast(SubProgDecl* subProgs) + = [cst2ast(subProg) | subProg <- subProgs]; +ASubProg cst2ast((SubProgDecl)``) + = function( + cst2ast(f.id), + cst2ast(f.params), + simpleType(cst2ast(f.returnType)), + cst2ast(f.locals), + cst2ast(f.body) + ); +ASubProg cst2ast((SubProgDecl)``) + = procedure( + cst2ast(p.id), + cst2ast(p.params), + cst2ast(p.locals), + cst2ast(p.body) + ); + +list[AParam] cst2ast(Parameters? optParams) + = [param | params <- optParams, param <- cst2ast(params)]; +list[AParam] cst2ast(Parameters params); + +// ---------------------------- Statement lists ----------------------------- + +ABlock cst2ast(CompoundStatement body) + = block([ + aStatement + | statementList <- body.statementList, + aStatement <- cst2ast(statementList) + ]); + +list[AStatement] cst2ast(StatementList sList) + = [cst2ast(sList.first)] + + [cst2ast(statement) | item <- sList.rest, /Statement statement := item]; + +// ----------------------------- Statements ---------------------------------- + +AStatement cst2ast((Statement)` := `) + = assignment(cst2ast(lhs), cst2ast(e)); + +AStatement cst2ast((Statement)``) + = call(cst2ast(subProgCall)); + +AStatement cst2ast((Statement)``) + = blockStatement(cst2ast(compoundStatement)); + +AStatement cst2ast((Statement)`skip`) + = skip(); + +AStatement cst2ast((Statement)`if then else `) + = \if(cst2ast(g), cst2ast(ifStmt), cst2ast(elseStmt)); + +AStatement cst2ast((Statement)`while do `) + = \while(cst2ast(g), cst2ast(body)); + +// -------------------------------- Calls ------------------------------------ +ASubProgCall cst2ast(SubProgCall call); + +// --------------------------------- Lhs ------------------------------------ +ALhs cst2ast(Lhs lhs); + +// ------------------------------ Expressions -------------------------------- + +AExpr cst2ast(Expr e); +AGuard cst2ast(Guard g); + +AValue cst2ast((Number)``) = integer(toInt("")); +AValue cst2ast((Number)``) = \real(toReal("")); + +// ----------------------------- Types --------------------------------------- + +AType cst2ast(TypeSpec \type); +ABasicType cst2ast(BasicType \type); + +AType inferType((Number)``) = simpleType(integerType()); +AType inferType((Number)``) = simpleType(realType()); \ No newline at end of file diff --git a/src/main/rascal/Syntax.rsc b/src/main/rascal/Syntax.rsc index 850e507..ac71857 100644 --- a/src/main/rascal/Syntax.rsc +++ b/src/main/rascal/Syntax.rsc @@ -10,20 +10,20 @@ extend lang::std::ASCII; /* A program consists of an id, followed by declarations followed by its body */ -start syntax Program = 'program' Id ";" ConstDecl* VarDecl* SubProgDecl* CompoundStatement "." +start syntax Program = 'program' Id name ";" ConstDecl* consts VarDecl* vars SubProgDecl* subProgs CompoundStatement main "." ; /* A constant's type is implicit by the value it's assigned */ -syntax ConstDecl = 'const' Id "=" Number ";" +syntax ConstDecl = 'const' Id id "=" Number value ";" ; /* You can declare multiple variables of the same type, in a simple declaration */ -syntax VarDecl = 'var' IdList ":" TypeSpec ";" +syntax VarDecl = 'var' IdList ids ":" TypeSpec type ";" ; syntax IdList = Id ("," Id)* @@ -48,8 +48,11 @@ syntax BasicType = 'integer' | 'real' Also note that we use SubProg to refer to both functions / procedures. */ -syntax SubProgDecl = 'function' Id Parameters ":" BasicType ";" VarDecl* CompoundStatement ";" - | 'procedure' Id Parameters? ";" VarDecl* CompoundStatement ";" +syntax SubProgDecl = FunctionDecl | ProcedureDecl + ; +syntax FunctionDecl = 'function' Id id Parameters params ":" BasicType returnType ";" VarDecl* locals CompoundStatement body";" + ; +syntax ProcedureDecl = 'procedure' Id id Parameters? params ";" VarDecl* locals CompoundStatement body ";" ; syntax Parameters = "(" ParameterList ")" @@ -66,9 +69,11 @@ syntax ParameterList = ParamList (";" ParamList)*; syntax ParamList = 'var'? IdList ":" TypeSpec ; -syntax CompoundStatement = 'begin' StatementList? 'end'; +syntax CompoundStatement = 'begin' StatementList? statementList 'end' + ; -syntax StatementList = Statement (";" Statement)*; +syntax StatementList = Statement first (";" Statement)* rest + ; /* We have: 1. Assignment statements @@ -146,21 +151,28 @@ lexical Id = ([a-z A-Z 0-9 _] !<< [a-z A-Z][a-z A-Z 0-9 _]* !>> [a-z A-Z /* Note that equality is represented as = and inequality is represented as <> */ -lexical Relop = "\<"|"\<="|"\>"|"\>="|"="|"\<\>"; +lexical Relop = "\<"|"\<="|"\>"|"\>="|"="|"\<\>" + ; /* Either: - 1. A series of digits (integer) - 2. An optional characteristic (right of the ".") and a mandatory mantissa (left of the ".") - This allows both .1 and 0.1 + 1. Integer: A series of digits + 2. Real: An optional characteristic (right of the ".") and a mandatory + mantissa (left of the ".") + This allows both .1 and 0.1 */ -lexical Number = Digit+ - | Digit* "." Digit+ +lexical Number = Integer + | Real + ; +lexical Integer = Digit+ + ; +lexical Real = Digit* "." Digit+ ; /* A comment is anything between a "{" and a "}" */ -lexical Comment = "{" ![}]* "}"; +lexical Comment = "{" ![}]* "}" + ; /* Layout essentially captures all characters between our main syntactic objects. You can read this as follows: @@ -194,7 +206,8 @@ lexical Comment = "{" ![}]* "}"; This is also in the documentation. See: https://www.rascal-mpl.org/docs/Rascal/Declarations/SyntaxDefinition/Disambiguation/Follow/ */ layout Standard - = WhitespaceOrComment* !>> [\u0009-\u000D \u0020 \u0085 \u00A0 \u1680 \u180E \u2000-\u200A \u2028 \u2029 \u202F \u205F \u3000 {}]; + = WhitespaceOrComment* !>> [\u0009-\u000D \u0020 \u0085 \u00A0 \u1680 \u180E \u2000-\u200A \u2028 \u2029 \u202F \u205F \u3000 {}] + ; lexical WhitespaceOrComment = whitespace: Whitespace diff --git a/src/main/rascal/Test.rsc b/src/main/rascal/Test.rsc index 5f688f2..383fa1d 100644 --- a/src/main/rascal/Test.rsc +++ b/src/main/rascal/Test.rsc @@ -4,6 +4,7 @@ import ParseTree; import Exception; import Syntax; import IO; +import vis::Text; void main() { // The input files @@ -17,6 +18,15 @@ void main() { } } +void testRobinProgram() { + loc robinLoc = |project://minipas/test/input/robin.pas|; + loc outLoc = |project://minipas/target/out/robinTree.txt|; + start[Program] parseTree = parse(#start[Program], robinLoc); + str printedTree = prettyTree(parseTree); + writeFile(outLoc, printedTree); + +} + /* Parses the file */ diff --git a/test/input/robin.pas b/test/input/robin.pas new file mode 100644 index 0000000..2778847 --- /dev/null +++ b/test/input/robin.pas @@ -0,0 +1,26 @@ +program robin; + +const cover = 1; + +const robins_skill = 0; + +const dogukan = 2; + +var player : integer; + +{ Prints 1 if Robin wins, 0 if it's a draw, -1 if robin loses } +procedure smash(at : integer; opponents_skill : integer) ; +BEGIN + if (robins_skill > opponents_skill) then + writeln(1) + else + if (robins_skill = opponents_skill) then + writeln(0) + else + writeln(-1) +END; + +BEGIN + player := dogukan; + smash(cover, player) +END. \ No newline at end of file