diff --git a/ops-intrinsics-replacer/README.md b/ops-intrinsics-replacer/README.md new file mode 100644 index 0000000..9c8bf3e --- /dev/null +++ b/ops-intrinsics-replacer/README.md @@ -0,0 +1,23 @@ +# A library to replace arithmetic operations with intrinsics + +The example first makes a call to the parser to get the AST, and then calls the ops-intrinsincs-replacer library (this library). + +## Run the example + +In order to run the example, first you need to run the parser server (from iclc2016/parser/): + +```node server-demo.js``` + +Then run the example (from iclc/ops-intrinsics-replacer/): + +```dart bin/example.dart``` + +The output should be: + +``` +AST received matches expected: +{"program":{"statements":[{"variableDeclaration":{"identifier":{"variable":"x"},"value":{"binaryOperation":{"operation":"+","lhs":{"int":100},"rhs":{"int":67}}}}},{"variableDeclaration":{"identifier":{"variable":"y"},"value":{"binaryOperation":{"operation":"*","lhs":{"int":50},"rhs":{"int":3}}}}},{"variableDeclaration":{"identifier":{"variable":"radius"},"value":{"binaryOperation":{"operation":"-","lhs":{"variable":"x"},"rhs":{"variable":"y"}}}}},{"whileLoop":{"condition":{"binaryOperation":{"operation":">","lhs":{"variable":"x"},"rhs":{"int":0}}},"statements":[{"call":{"receiver":{"variable":"drawCircle"},"args":[{"variable":"x"},{"binaryOperation":{"operation":"+","lhs":{"variable":"y"},"rhs":{"int":5}}},{"variable":"radius"}]}},{"variableAssignment":{"identifier":{"variable":"x"},"value":{"binaryOperation":{"operation":"-","lhs":{"variable":"x"},"rhs":{"int":10}}}}}]}}]}} +----- +AST processed matches expected: +{"program":{"statements":[{"variableDeclaration":{"identifier":{"variable":"x"},"value":{"call":{"receiver":{"variable":"intrinsics.add"},"args":[{"int":100},{"int":67}]}}}},{"variableDeclaration":{"identifier":{"variable":"y"},"value":{"call":{"receiver":{"variable":"intrinsics.mul"},"args":[{"int":50},{"int":3}]}}}},{"variableDeclaration":{"identifier":{"variable":"radius"},"value":{"call":{"receiver":{"variable":"intrinsics.sub"},"args":[{"variable":"x"},{"variable":"y"}]}}}},{"whileLoop":{"condition":{"binaryOperation":{"operation":">","lhs":{"variable":"x"},"rhs":{"int":0}}},"statements":[{"call":{"receiver":{"variable":"drawCircle"},"args":[{"variable":"x"},{"call":{"receiver":{"variable":"intrinsics.add"},"args":[{"variable":"y"},{"int":5}]}},{"variable":"radius"}]}},{"variableAssignment":{"identifier":{"variable":"x"},"value":{"call":{"receiver":{"variable":"intrinsics.sub"},"args":[{"variable":"x"},{"int":10}]}}}}]}}]}} +``` diff --git a/ops-intrinsics-replacer/bin/example.dart b/ops-intrinsics-replacer/bin/example.dart new file mode 100644 index 0000000..8979c09 --- /dev/null +++ b/ops-intrinsics-replacer/bin/example.dart @@ -0,0 +1,46 @@ +import 'dart:io'; +import 'dart:async'; +import 'dart:convert' show UTF8, JSON; + +import 'package:ops_intrinsics_replacer/ops-intrinsincs-replacer.dart' as opsreplacer; + +Future main() async { + String sampleProgram = ''' + var x = 100 + 67; + var y = 50 * 3; + var radius = x - y; + while(x > 0) { + drawCircle(x, y + 5, radius); + x = x - 10; + }'''; + String expectedAST_JSON = '{"program":{"statements":[{"variableDeclaration":{"identifier":{"variable":"x"},"value":{"binaryOperation":{"operation":"+","lhs":{"int":100},"rhs":{"int":67}}}}},{"variableDeclaration":{"identifier":{"variable":"y"},"value":{"binaryOperation":{"operation":"*","lhs":{"int":50},"rhs":{"int":3}}}}},{"variableDeclaration":{"identifier":{"variable":"radius"},"value":{"binaryOperation":{"operation":"-","lhs":{"variable":"x"},"rhs":{"variable":"y"}}}}},{"whileLoop":{"condition":{"binaryOperation":{"operation":">","lhs":{"variable":"x"},"rhs":{"int":0}}},"statements":[{"call":{"receiver":{"variable":"drawCircle"},"args":[{"variable":"x"},{"binaryOperation":{"operation":"+","lhs":{"variable":"y"},"rhs":{"int":5}}},{"variable":"radius"}]}},{"variableAssignment":{"identifier":{"variable":"x"},"value":{"binaryOperation":{"operation":"-","lhs":{"variable":"x"},"rhs":{"int":10}}}}}]}}]}}'; + String expectedProcessedAST_JSON = '{"program":{"statements":[{"variableDeclaration":{"identifier":{"variable":"x"},"value":{"call":{"receiver":{"variable":"intrinsics.add"},"args":[{"int":100},{"int":67}]}}}},{"variableDeclaration":{"identifier":{"variable":"y"},"value":{"call":{"receiver":{"variable":"intrinsics.mul"},"args":[{"int":50},{"int":3}]}}}},{"variableDeclaration":{"identifier":{"variable":"radius"},"value":{"call":{"receiver":{"variable":"intrinsics.sub"},"args":[{"variable":"x"},{"variable":"y"}]}}}},{"whileLoop":{"condition":{"binaryOperation":{"operation":">","lhs":{"variable":"x"},"rhs":{"int":0}}},"statements":[{"call":{"receiver":{"variable":"drawCircle"},"args":[{"variable":"x"},{"call":{"receiver":{"variable":"intrinsics.add"},"args":[{"variable":"y"},{"int":5}]}},{"variable":"radius"}]}},{"variableAssignment":{"identifier":{"variable":"x"},"value":{"call":{"receiver":{"variable":"intrinsics.sub"},"args":[{"variable":"x"},{"int":10}]}}}}]}}]}}'; + + var host = '127.0.0.1'; + var port = 8080; + var path = '/generate_ast'; + + var request = await new HttpClient().post(host, port, path); + request.headers.contentType = ContentType.JSON; + request.write(JSON.encode({'code': sampleProgram})); + HttpClientResponse response = await request.close(); + await for (var contents in response.transform(UTF8.decoder)) { + var receivedAST = JSON.decode(contents); + var receivedAST_JSON = JSON.encode(receivedAST); // just to make sure tests pass, as expectedAST and expectedProcessedAST have been generated with JSON.encode(). + if (receivedAST_JSON == expectedAST_JSON) { + print('AST received matches expected:\n${expectedAST_JSON}'); + } else { + print('AST received does not match!\nExpected:\n${expectedAST_JSON}\nReceived:\n${receivedAST_JSON}'); + return; + } + print('-----'); + opsreplacer.processAST(receivedAST); + var processedAST_JSON = JSON.encode(receivedAST); + if (processedAST_JSON == expectedProcessedAST_JSON) { + print('AST processed matches expected:\n${expectedProcessedAST_JSON}'); + } else { + print('AST processed does not match!\nExpected:\n${expectedProcessedAST_JSON}\Processed:\n${processedAST_JSON}'); + return; + } + } +} diff --git a/ops-intrinsics-replacer/lib/ops-intrinsincs-replacer.dart b/ops-intrinsics-replacer/lib/ops-intrinsincs-replacer.dart new file mode 100644 index 0000000..6f309da --- /dev/null +++ b/ops-intrinsics-replacer/lib/ops-intrinsincs-replacer.dart @@ -0,0 +1,42 @@ +library ops_intrinsics_replacer; + +Map correspondences = { + "+": "intrinsics.add", + "-": "intrinsics.sub", + "*": "intrinsics.mul", + "/": "intrinsics.div", +}; + +processAST(ast) { + // Walk over the AST and if coming across a "binaryOperation", replace it with + // a "call" expression. + if (ast is Map) { + if (ast.keys.length != 1) { // it's not a binaryOperation. + ast.forEach((key, value) => processAST(value)); + } else if (ast.keys.first == "binaryOperation") { // it's a binaryOperation + var binaryOpValue = ast["binaryOperation"]; + if (correspondences.keys.contains(binaryOpValue["operation"])) { // it an operation that can be replaced with intrinsics + var callValue = { + "receiver": { + "variable": correspondences[binaryOpValue["operation"]], + }, + "args": [ + binaryOpValue["lhs"], + binaryOpValue["rhs"], + ] + }; + ast["call"] = callValue; + ast.remove("binaryOperation"); + processAST(callValue); + } else { + processAST(binaryOpValue); + } + } else { + processAST(ast[ast.keys.first]); // process whatever operation is here + } + } else if (ast is List) { + for (var astNode in ast) { + processAST(astNode); + } + } else {} +} diff --git a/ops-intrinsics-replacer/pubspec.yaml b/ops-intrinsics-replacer/pubspec.yaml new file mode 100644 index 0000000..7927a02 --- /dev/null +++ b/ops-intrinsics-replacer/pubspec.yaml @@ -0,0 +1,3 @@ +name: ops_intrinsics_replacer +description: A library that tranforms arithmetic operations into intrinsic function calls. +dependencies: