############################################################################# # Grammar and interpreter for a mini version of Basic. # Author: Robert Feldt # # The grammar is based on the minibasic example in SableCC. # # Here's the copyright notice from the original file SableCC file: # # Copyright (C) 1997, 1998, 1999 J-Meg inc. All rights reserved. # # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published # by the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # # This file is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this file (in the file "COPYING-LESSER"); if not, # write to the Free Software Foundation, Inc., 59 Temple Place, # Suite 330, Boston, MA 02111-1307 USA # # If you have any question, send an electronic message to # Etienne M. Gagnon, M.Sc. (egagnon@j-meg.com), or write to: # # J-Meg inc. # 11348 Brunet # Montreal-Nord (Quebec) # H1G 5G1 Canada ############################################################################# require 'packrat/grammar' module MiniBasic class Interpreter def initialize # For variables and their values. Default value is 0. @vars = Hash.new(0) end def eval(sexpr) case sexpr.first when :Statements sexpr.statements.each {|stmt| mb_eval(stmt)} when "If" if mb_eval(sexpr.condition) # What is true and false in basic? mb_eval(sexpr.statements) elsif sexpr.optelse mb_eval(sexpr.optelse[2]) end when "For" for i in (mb_eval(sexpr.from)..mb_eval(sexpr.to)) $vars[sexpr.ident.id] = i mb_eval(sexpr.statements) end when "Read" print "? "; STDOUT.flush $vars[sexpr.ident.id] = STDIN.gets.to_i # Error catching?! when "Print" print mb_eval(sexpr.message); STDOUT.flush when "PrintLn" print "\n"; STDOUT.flush when "Assignment" $vars[sexpr.ident.id] = mb_eval(sexpr.expression) when "Condition" map = {">" => :>, "<" => :<, "=" => :==} mb_eval(sexpr.left).send(map[sexpr.op], mb_eval(sexpr.right)) when "BinExpr" map = {"+"=>:+, "-"=>:-, "*"=>:*, "/"=>"/".intern, "MOD"=>"%".intern } mb_eval(sexpr.left).send(map[sexpr.op], mb_eval(sexpr.right)) when "String" sexpr.value[1..-2] # Skip leading and trailing double quotes when "Identifier" $vars[sexpr.id] when "Number" sexpr.lexeme.to_i end end end Grammar = Packrat::Grammar.new do start_symbol :Program # Spacing (S) and Forced Spacing (FS) S = hidden(/\s*/) FS = hidden(/\s\s*/) prod :Program, [S, :Statements, eos(), lift(0)] prod :Statements, [plus(:Statement), lift(0)] rule :Statement, [ ['IF', FS, :Condition, FS, 'THEN', FS, :Statements, FS, maybe(:OptElse), S, 'ENDIF', S, ast(:If)], ['FOR', FS, :Identifier, S, ':=', S, :Expr, FS, 'TO', FS, :Expr, S, :Statements, S, 'NEXT', S, ast(:For)], ['READ', FS, :Identifier, S, ast(:Read)], ['PRINTLN', S, ast(:PrintLn)], ['PRINT', FS, any(:Expr, :String), S, ast(:Print)], [:Identifier, S, ':=', S, :Expr, S, ast(:Assign)], ] prod :OptElse, ['ELSE', FS, :Statements, lift(1)] prod :Condition, [:Expr, S, any('<', '>', '='), S, :Expr, ast(:Cond)] # This is crude! No precedence levels or handling of associativity. rule :Expr, [ [:BaseExpr, S, any('+', '-', '*', '/', 'MOD'), S, :BaseExpr, ast(:OpExpr) ], [:BaseExpr, lift(0)], ] rule :BaseExpr, [ [:Number, lift(0)], [:Identifier, lift(0)], ['(', S, :Expr, S, ')', lift(2)], ] prod :String, [/"[^"]*"/, lift(0)] prod :Identifier, [/[A-Z]([A-Z0-9])*/, lift(0) {|r| r.intern}] prod :Number, [/[0-9]+/, lift(0) {|r| r.to_i}] end Parser = Grammar.interpreting_parser end