MysoreScript
main.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 David Chisnall
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy of
5  * this software and associated documentation files (the "Software"), to deal in
6  * the Software without restriction, including without limitation the rights to
7  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8  * the Software, and to permit persons to whom the Software is furnished to do so,
9  * subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20  */
21 #include <iostream>
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <stdlib.h>
25 #include <sys/resource.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <gc.h>
29 #include <readline.h>
30 #include "parser.hh"
31 #include "interpreter.hh"
32 
36 static bool enableTiming = false;
37 
42 static void logTimeSince(clock_t c1, const char *msg)
43 {
44  if (!enableTiming) { return; }
45  clock_t c2 = clock();
46  struct rusage r;
47  getrusage(RUSAGE_SELF, &r);
48  auto oldLocale = std::cerr.imbue(std::locale("en_GB.UTF-8"));
49  std::cerr << msg << " took "
50  << (static_cast<double>(c2) - static_cast<double>(c1)) / static_cast<double>(CLOCKS_PER_SEC)
51  << " seconds. Peak used " << r.ru_maxrss/1024 << "KB." << std::endl;
52  std::cerr.imbue(oldLocale);
53 }
57 void usage(const char *cmd)
58 {
59  std::cerr << "usage: " << cmd << " [-himt] [-f {file name}]" << std::endl
60  << " -h Display this help" << std::endl
61  << " -c Force compilation" << std::endl
62  << " -i Interpreter, enable REPL mode" << std::endl
63  << " -m Display memory usage stats on exit" << std::endl
64  << " -t Display timing information" << std::endl
65  << " -f {file} Load and execute file" << std::endl;
66 }
67 
68 int main(int argc, char **argv)
69 {
70  clock_t c1;
71  // Are we in read-evaluate-print-loop mode?
72  bool repl = false;
73  // Are memory usage statistics requested?
74  bool memstats = false;
75  // What file should we print?
76  const char *file = nullptr;
77  if (argc < 1)
78  {
79  usage(argv[0]);
80  return EXIT_FAILURE;
81  }
82  int c;
83  pegmatite::ErrorReporter err =
84  [](const pegmatite::InputRange &r, std::string s) {
85  std::cerr << "Syntax error: \n line " << r.start.line
86  << ", column " << r.start.col;
87  };
88  // Parse the options that we understand
89  while ((c = getopt(argc, argv, "chmitf:")) != -1)
90  {
91  switch (c)
92  {
93  case 'i':
94  repl = true;
95  break;
96  case 'f':
97  file = optarg;
98  break;
99  case 't':
100  enableTiming = true;
101  break;
102  case 'm':
103  memstats = true;
104  break;
105  case 'h':
106  usage(argv[0]);
107  break;
108  case 'c':
110  break;
111  }
112  }
113  c1 = clock();
114  //Initialise the garbage collection library. This must be called before
115  //any objects are allocated.
116  GC_init();
117 
118  // Set up a parser and interpreter context to use.
121  // Log the time taken for all of the program setup.
122  logTimeSince(c1, "Setup");
123  // The AST for the program loaded from a file, if there is one
124  std::unique_ptr<AST::Statements> ast = 0;
125  // If a filename was specified, then try to parse and execute it.
126  if (file)
127  {
128  // Open the file
129  pegmatite::AsciiFileInput input(open(file, O_RDONLY));
130  c1 = clock();
131  // Parse one or more statements, report errors if there are any
132  if (!p.parse(input, p.g.statements, p.g.ignored, err, ast))
133  {
134  return EXIT_FAILURE;
135  }
136  logTimeSince(c1, "Parsing program");
137  c1 = clock();
138  // Now interpret the parsed
139  ast->interpret(C);
140  logTimeSince(c1, "Executing program");
141  }
142  // Keep all of the ASTs that we've parsed in the REPL environment in case
143  // anything is referencing them.
144  std::vector<std::unique_ptr<AST::Statements>> replASTs;
145 #ifdef HAVE_READLINE
146  // Initialise libedit
147  rl_initialize();
148 #endif
149  // As long as we're in REPL mode, read, evaluate and loop - we don't
150  // actually print the result, so technically this is REL...
151  while (repl)
152  {
153  c1 = clock();
154  GC_gcollect();
155  logTimeSince(c1, "Garbage collection");
156 #ifdef HAVE_READLINE
157  // Print the prompt and get a line. Use a unique_ptr so that we don't
158  // have to worry about freeing the memory.
159  std::unique_ptr<char, decltype(free)*> line(readline("\nMysoreScript> "), free);
160  // If it was an empty line, exit REPL mode
161  if (line == nullptr || line.get()[0] == '\0')
162  {
163  break;
164  }
165  // Add this to the history.
166  add_history(line.get());
167  // Convert to a string so that the readline and non-readline codepaths
168  // are the same after this point.
169  std::string buffer(line.get());
170 #else
171  std::string buffer;
172  // Print the prompt
173  std::cout << "\nMysoreScript> ";
174  // Get a line
175  std::getline(std::cin, buffer);
176  // If it was an empty line, exit REPL mode
177  if (buffer.size() == 0)
178  {
179  break;
180  }
181 #endif
182  // Parse the line
183  pegmatite::StringInput input(std::move(buffer));
184  std::unique_ptr<AST::Statements> ast = 0;
185  c1 = clock();
186  if (!p.parse(input, p.g.statements, p.g.ignored, err, ast))
187  {
188  continue;
189  }
190  logTimeSince(c1, "Parsing program");
191  c1 = clock();
192  // Interpret the resulting AST
193  ast->interpret(C);
194  logTimeSince(c1, "Executing program");
195  // Keep the AST around - it may contain things that we refer to later
196  // (e.g. functions / classes).
197  replASTs.push_back(std::move(ast));
198  }
199  // Print some memory usage stats, if requested.
200  if (memstats)
201  {
202  std::cerr.imbue(std::locale("en_GB.UTF-8"));
203  std::cerr << "Allocated a total of " << GC_get_total_bytes()
204  << " bytes during execution." << std::endl;
205  std::cerr << "GC heap size: " << GC_get_heap_size() << " bytes."
206  << std::endl;
207  ast = nullptr;
208  replASTs.clear();
209  GC_gcollect_and_unmap();
210  std::cerr << "After collection, GC heap size: " << GC_get_heap_size()
211  << " bytes." << std::endl;
212  }
213  return 0;
214 }
Rule ignored
Rule for treating both comments and whitespace as ignored tokens.
Definition: grammar.hh:33
void interpret(Interpreter::Context &c)
Interprets each of the statements in turn.
Definition: interpreter.cc:376
Class representing a parser for the MysoreScript language.
Definition: parser.hh:10
bool forceCompiler
Force the compiler to run.
Definition: interpreter.cc:7
Rule statements
A list of statements: the top-level for programs in this grammar.
Definition: grammar.hh:216
int main(int argc, char **argv)
Definition: main.cc:68
void usage(const char *cmd)
Print the usage message.
Definition: main.cc:57
const MysoreScriptGrammar & g
The grammar that this parser uses.
Definition: parser.hh:44