1/** 2 * Example Whiley program, taken from the Whiley benchmark suite. 3 * https://github.com/Whiley/WyBench/blob/master/src/101_interpreter/Main.whiley 4 */ 5 6import whiley.lang.System 7import whiley.lang.Int 8import whiley.io.File 9import string from whiley.lang.ASCII 10import char from whiley.lang.ASCII 11 12// ==================================================== 13// A simple calculator for expressions 14// ==================================================== 15 16constant ADD is 0 17constant SUB is 1 18constant MUL is 2 19constant DIV is 3 20 21// binary operation 22type BOp is (int x) where ADD <= x && x <= DIV 23type BinOp is { BOp op, Expr lhs, Expr rhs } 24 25// variables 26type Var is { string id } 27 28// list access 29type ListAccess is { 30 Expr src, 31 Expr index 32} 33 34// expression tree 35type Expr is int | // constant 36 Var | // variable 37 BinOp | // binary operator 38 Expr[] | // array constructor 39 ListAccess // list access 40 41// values 42type Value is int | Value[] 43 44// stmts 45type Print is { Expr rhs } 46type Set is { string lhs, Expr rhs } 47type Stmt is Print | Set 48 49// ==================================================== 50// Expression Evaluator 51// ==================================================== 52 53type RuntimeError is { string msg } 54type Environment is [{string k, Value v}] 55 56// Evaluate an expression in a given environment reducing either to a 57// value, or a runtime error. The latter occurs if evaluation gets 58// "stuck" (e.g. expression is // not well-formed) 59function evaluate(Expr e, Environment env) -> Value | RuntimeError: 60 // 61 if e is int: 62 return e 63 else if e is Var: 64 return env[e.id] 65 else if e is BinOp: 66 Value|RuntimeError lhs = evaluate(e.lhs, env) 67 Value|RuntimeError rhs = evaluate(e.rhs, env) 68 // check if stuck 69 if !(lhs is int && rhs is int): 70 return {msg: "arithmetic attempted on non-numeric value"} 71 // switch statement would be good 72 if e.op == ADD: 73 return lhs + rhs 74 else if e.op == SUB: 75 return lhs - rhs 76 else if e.op == MUL: 77 return lhs * rhs 78 else if rhs != 0: 79 return lhs / rhs 80 return {msg: "divide-by-zero"} 81 else if e is Expr[]: 82 [Value] r = [] 83 for i in e: 84 Value|RuntimeError v = evaluate(i, env) 85 if v is RuntimeError: 86 return v 87 else: 88 r = r ++ [v] 89 return r 90 else if e is ListAccess: 91 Value|RuntimeError src = evaluate(e.src, env) 92 Value|RuntimeError index = evaluate(e.index, env) 93 // santity checks 94 if src is [Value] && index is int && index >= 0 && index < |src|: 95 return src[index] 96 else: 97 return {msg: "invalid list access"} 98 else: 99 return 0 // dead-code 100 101// ==================================================== 102// Expression Parser 103// ==================================================== 104 105type State is { string input, int pos } 106type SyntaxError is { string msg, int start, int end } 107 108function SyntaxError(string msg, int start, int end) -> SyntaxError: 109 return { msg: msg, start: start, end: end } 110 111// Top-level parse method 112function parse(State st) -> (Stmt,State)|SyntaxError: 113 // 114 Var keyword, Var v 115 Expr e 116 int start = st.pos 117 // 118 keyword,st = parseIdentifier(st) 119 switch keyword.id: 120 case "print": 121 any r = parseAddSubExpr(st) 122 if !(r is SyntaxError): 123 e,st = r 124 return {rhs: e},st 125 else: 126 return r // error case 127 case "set": 128 st = parseWhiteSpace(st) 129 v,st = parseIdentifier(st) 130 any r = parseAddSubExpr(st) 131 if !(r is SyntaxError): 132 e,st = r 133 return {lhs: v.id, rhs: e},st 134 else: 135 return r // error case 136 default: 137 return SyntaxError("unknown statement",start,st.pos-1) 138 139function parseAddSubExpr(State st) -> (Expr, State)|SyntaxError: 140 // 141 Expr lhs, Expr rhs 142 // First, pass left-hand side 143 any r = parseMulDivExpr(st) 144 // 145 if r is SyntaxError: 146 return r 147 // 148 lhs,st = r 149 st = parseWhiteSpace(st) 150 // Second, see if there is a right-hand side 151 if st.pos < |st.input| && st.input[st.pos] == '+': 152 // add expression 153 st.pos = st.pos + 1 154 r = parseAddSubExpr(st) 155 if !(r is SyntaxError): 156 rhs,st = r 157 return {op: ADD, lhs: lhs, rhs: rhs},st 158 else: 159 return r 160 else if st.pos < |st.input| && st.input[st.pos] == '-': 161 // subtract expression 162 st.pos = st.pos + 1 163 r = parseAddSubExpr(st) 164 if !(r is SyntaxError): 165 rhs,st = r 166 return {op: SUB, lhs: lhs, rhs: rhs},st 167 else: 168 return r 169 // No right-hand side 170 return (lhs,st) 171 172function parseMulDivExpr(State st) -> (Expr, State)|SyntaxError: 173 // First, parse left-hand side 174 Expr lhs, Expr rhs 175 any r = parseTerm(st) 176 if r is SyntaxError: 177 return r 178 // 179 lhs,st = r 180 st = parseWhiteSpace(st) 181 // Second, see if there is a right-hand side 182 if st.pos < |st.input| && st.input[st.pos] == '*': 183 // add expression 184 st.pos = st.pos + 1 185 r = parseMulDivExpr(st) 186 if !(r is SyntaxError): 187 rhs,st = r 188 return {op: MUL, lhs: lhs, rhs: rhs}, st 189 else: 190 return r 191 else if st.pos < |st.input| && st.input[st.pos] == '/': 192 // subtract expression 193 st.pos = st.pos + 1 194 r = parseMulDivExpr(st) 195 if !(r is SyntaxError): 196 rhs,st = r 197 return {op: DIV, lhs: lhs, rhs: rhs}, st 198 else: 199 return r 200 // No right-hand side 201 return (lhs,st) 202 203function parseTerm(State st) -> (Expr, State)|SyntaxError: 204 // 205 st = parseWhiteSpace(st) 206 if st.pos < |st.input|: 207 if ASCII.isLetter(st.input[st.pos]): 208 return parseIdentifier(st) 209 else if ASCII.isDigit(st.input[st.pos]): 210 return parseNumber(st) 211 else if st.input[st.pos] == '[': 212 return parseList(st) 213 // 214 return SyntaxError("expecting number or variable",st.pos,st.pos) 215 216function parseIdentifier(State st) -> (Var, State): 217 // 218 string txt = "" 219 // inch forward until end of identifier reached 220 while st.pos < |st.input| && ASCII.isLetter(st.input[st.pos]): 221 txt = txt ++ [st.input[st.pos]] 222 st.pos = st.pos + 1 223 return ({id:txt}, st) 224 225function parseNumber(State st) -> (Expr, State)|SyntaxError: 226 // inch forward until end of identifier reached 227 int start = st.pos 228 while st.pos < |st.input| && ASCII.isDigit(st.input[st.pos]): 229 st.pos = st.pos + 1 230 // 231 int|null iv = Int.parse(st.input[start..st.pos]) 232 if iv == null: 233 return SyntaxError("Error parsing number",start,st.pos) 234 else: 235 return iv, st 236 237function parseList(State st) -> (Expr, State)|SyntaxError: 238 // 239 st.pos = st.pos + 1 // skip '[' 240 st = parseWhiteSpace(st) 241 [Expr] l = [] // initial list 242 bool firstTime = true 243 while st.pos < |st.input| && st.input[st.pos] != ']': 244 if !firstTime && st.input[st.pos] != ',': 245 return SyntaxError("expecting comma",st.pos,st.pos) 246 else if !firstTime: 247 st.pos = st.pos + 1 // skip ',' 248 firstTime = false 249 any r = parseAddSubExpr(st) 250 if r is SyntaxError: 251 return r 252 else: 253 Expr e 254 e,st = r 255 // perform annoying error check 256 l = l ++ [e] 257 st = parseWhiteSpace(st) 258 st.pos = st.pos + 1 259 return l,st 260 261// Parse all whitespace upto end-of-file 262function parseWhiteSpace(State st) -> State: 263 while st.pos < |st.input| && ASCII.isWhiteSpace(st.input[st.pos]): 264 st.pos = st.pos + 1 265 return st 266 267// ==================================================== 268// Main Method 269// ==================================================== 270 271public method main(System.Console sys): 272 if(|sys.args| == 0): 273 sys.out.println("no parameter provided!") 274 else: 275 File.Reader file = File.Reader(sys.args[0]) 276 string input = ASCII.fromBytes(file.readAll()) 277 278 Environment env = Environment() 279 State st = {pos: 0, input: input} 280 while st.pos < |st.input|: 281 Stmt s 282 any r = parse(st) 283 if r is SyntaxError: 284 sys.out.println("syntax error: " ++ r.msg) 285 return 286 s,st = r 287 Value|RuntimeError v = evaluate(s.rhs,env) 288 if v is RuntimeError: 289 sys.out.println("runtime error: " ++ v.msg) 290 return 291 if s is Set: 292 env[s.lhs] = v 293 else: 294 sys.out.println(r) 295 st = parseWhiteSpace(st) 296 297