implement boolean
This commit is contained in:
@ -7,5 +7,42 @@ def pass:
|
||||
object inttrait:
|
||||
def add(other):
|
||||
self $int_add(other)
|
||||
|
||||
# Project: Sugar
|
||||
def sub(other):
|
||||
self $int_sub(other)
|
||||
def mul(other):
|
||||
self $int_mul(other)
|
||||
def div(other):
|
||||
self $int_div(other)
|
||||
def mod(other):
|
||||
self $int_mod(other)
|
||||
def inc:
|
||||
self $int_inc
|
||||
|
||||
# Project: Boolean
|
||||
def eq(other):
|
||||
self $int_eq(other)
|
||||
def leq(other):
|
||||
self $int_leq(other)
|
||||
def geq(other):
|
||||
self $int_geq(other)
|
||||
def greater(other):
|
||||
self $int_greater(other)
|
||||
def less(other):
|
||||
self $int_less(other)
|
||||
|
||||
def tobool:
|
||||
self $int_tobool
|
||||
|
||||
# Project: Boolean
|
||||
object booltrait:
|
||||
def and(other):
|
||||
self $bool_and(other)
|
||||
def or(other):
|
||||
self $bool_or(other)
|
||||
def not:
|
||||
self $bool_not
|
||||
|
||||
def toint:
|
||||
self $bool_toint
|
||||
|
@ -64,6 +64,9 @@ class Interpreter(object):
|
||||
elif opcode == compile.INT_LITERAL:
|
||||
w_value = self.space.newint(oparg)
|
||||
stack.append(w_value)
|
||||
elif opcode == compile.BOOL_LITERAL: # Project: Boolean
|
||||
w_value = self.space.newbool(oparg) # oparg is 1 or 0
|
||||
stack.append(w_value)
|
||||
elif opcode == compile.MAKE_FUNCTION:
|
||||
bc = bytecode.subbytecodes[oparg]
|
||||
w_method = self.space.definemethod(name=bc.name, code=bc, w_target=w_context)
|
||||
|
@ -109,23 +109,24 @@ import simpleast
|
||||
|
||||
# ---------- bytecodes ----------
|
||||
|
||||
INT_LITERAL = 2 # integer value
|
||||
ASSIGNMENT = 4 # index of attrname
|
||||
METHOD_LOOKUP = 5 # index of method name
|
||||
METHOD_CALL = 6 # number of arguments
|
||||
PRIMITIVE_METHOD_CALL = 7 # number of the primitive
|
||||
MAKE_FUNCTION = 8 # bytecode literal index
|
||||
MAKE_OBJECT = 9 # index of object name
|
||||
ASSIGNMENT_APPEND_PARENT = 10 # index of parentname
|
||||
MAKE_OBJECT_CALL = 11 # bytecode literal index
|
||||
JUMP_IF_FALSE = 12 # offset
|
||||
JUMP = 13 # offset
|
||||
GET_LOCAL = 15 # index of attrname (optimization)
|
||||
SET_LOCAL = 16 # index of attrname (optimization)
|
||||
BOOL_LITERAL = 1 # 1 or 0 # Project: Boolean
|
||||
INT_LITERAL = 2 # integer value
|
||||
ASSIGNMENT = 4 # index of attrname
|
||||
METHOD_LOOKUP = 5 # index of method name
|
||||
METHOD_CALL = 6 # number of arguments
|
||||
PRIMITIVE_METHOD_CALL = 7 # number of the primitive
|
||||
MAKE_FUNCTION = 8 # bytecode literal index
|
||||
MAKE_OBJECT = 9 # index of object name
|
||||
ASSIGNMENT_APPEND_PARENT = 10 # index of parentname
|
||||
MAKE_OBJECT_CALL = 11 # bytecode literal index
|
||||
JUMP_IF_FALSE = 12 # offset
|
||||
JUMP = 13 # offset
|
||||
GET_LOCAL = 15 # index of attrname (optimization)
|
||||
SET_LOCAL = 16 # index of attrname (optimization)
|
||||
|
||||
IMPLICIT_SELF = 32 # (no argument)
|
||||
POP = 33 # (no argument)
|
||||
DUP = 34 # (no argument)
|
||||
IMPLICIT_SELF = 32 # (no argument)
|
||||
POP = 33 # (no argument)
|
||||
DUP = 34 # (no argument)
|
||||
|
||||
opcode_names = [None] * 256
|
||||
for key, value in list(globals().items()):
|
||||
@ -133,11 +134,11 @@ for key, value in list(globals().items()):
|
||||
opcode_names[value] = key
|
||||
|
||||
|
||||
|
||||
def hasarg(opcode):
|
||||
""" Helper function to determine whether an opcode has an argument."""
|
||||
return opcode < 32
|
||||
|
||||
|
||||
def isjump(opcode):
|
||||
""" Helper function to determine whether an opcode is a jump."""
|
||||
return opcode == JUMP_IF_FALSE or opcode == JUMP
|
||||
@ -188,6 +189,7 @@ def compile(ast, argumentnames=[], name=None):
|
||||
|
||||
|
||||
stack_effects = {
|
||||
BOOL_LITERAL: 1, # Project: Boolean
|
||||
INT_LITERAL: 1,
|
||||
ASSIGNMENT: -1,
|
||||
METHOD_LOOKUP: 1,
|
||||
@ -219,10 +221,10 @@ class Compiler(object):
|
||||
for name, index in list(self.symbols.items()):
|
||||
symbols[index] = name
|
||||
result = Bytecode(''.join(self.code),
|
||||
funcname,
|
||||
symbols,
|
||||
self.subbytecodes,
|
||||
numargs, self.max_stackdepth)
|
||||
funcname,
|
||||
symbols,
|
||||
self.subbytecodes,
|
||||
numargs, self.max_stackdepth)
|
||||
assert self.stackdepth == 1
|
||||
return result
|
||||
|
||||
@ -257,10 +259,10 @@ class Compiler(object):
|
||||
return len(self.code)
|
||||
|
||||
def set_target_position(self, oldposition, newtarget):
|
||||
offset = newtarget - (oldposition+5)
|
||||
offset = newtarget - (oldposition + 5)
|
||||
i = 0
|
||||
for c in self.encode4(offset):
|
||||
self.code[oldposition+1+i] = c
|
||||
self.code[oldposition + 1 + i] = c
|
||||
i += 1
|
||||
|
||||
def encode4(self, value):
|
||||
@ -274,13 +276,16 @@ class Compiler(object):
|
||||
self.symbols[symbol] = len(self.symbols)
|
||||
return self.symbols[symbol]
|
||||
|
||||
|
||||
def compile(self, ast, needsresult=True):
|
||||
return getattr(self, "compile_" + ast.__class__.__name__)(ast, needsresult)
|
||||
|
||||
def compile_IntLiteral(self, astnode, needsresult):
|
||||
self.emit(INT_LITERAL, astnode.value)
|
||||
|
||||
# Project: Boolean
|
||||
def compile_BooleanLiteral(self, astnode, needsresult):
|
||||
self.emit(BOOL_LITERAL, astnode.value)
|
||||
|
||||
def compile_ImplicitSelf(self, astnode, needsresult):
|
||||
self.emit(IMPLICIT_SELF)
|
||||
|
||||
@ -318,7 +323,7 @@ class Compiler(object):
|
||||
expected_args = primitives.get_number_of_arguments_of_primitive(index)
|
||||
if not (len(astnode.arguments) == expected_args):
|
||||
raise TypeError(
|
||||
"Expected {ex} arguments, received {re}.".format(ex=expected_args, re=len(astnode.arguments)))
|
||||
"Expected {ex} arguments, received {re}.".format(ex=expected_args, re=len(astnode.arguments)))
|
||||
self.compile(astnode.receiver)
|
||||
for arg in astnode.arguments:
|
||||
self.compile(arg)
|
||||
|
@ -74,16 +74,17 @@ class W_NormalObject(AbstractObject):
|
||||
|
||||
|
||||
class W_Integer(AbstractObject):
|
||||
def __init__(self, value, space=None):
|
||||
self.value = value
|
||||
def __init__(self, value, space=None, trait="inttrait"):
|
||||
self.value = int(value)
|
||||
self.space = space
|
||||
self.__trait = trait # used this to extend from W_Integer
|
||||
|
||||
def getparents(self):
|
||||
if self.space is None:
|
||||
return [] # for tests
|
||||
inttrait = self.space.getbuiltin('inttrait')
|
||||
assert inttrait is not None, 'O_o bogus state'
|
||||
return [inttrait]
|
||||
trait = self.space.getbuiltin(self.__trait)
|
||||
assert trait is not None, 'O_o bogus state'
|
||||
return [trait]
|
||||
|
||||
def hasslot(self, name):
|
||||
return False
|
||||
@ -97,6 +98,12 @@ class W_Integer(AbstractObject):
|
||||
return self.value != 0
|
||||
|
||||
|
||||
# Project: Boolean
|
||||
class W_Boolean(W_Integer): # don't know if extending is good idea
|
||||
def __init__(self, value, space=None):
|
||||
super().__init__(int(value), space=space, trait="booltrait")
|
||||
|
||||
|
||||
class W_Method(W_NormalObject):
|
||||
|
||||
def __init__(self, code, *args, **kwargs):
|
||||
|
@ -1,4 +1,4 @@
|
||||
from objmodel import W_Integer
|
||||
from objmodel import W_Integer, W_Boolean
|
||||
from objmodel import W_Method
|
||||
from objmodel import W_NormalObject
|
||||
|
||||
@ -49,6 +49,10 @@ class ObjectSpace(object):
|
||||
def newint(self, value):
|
||||
return W_Integer(value, space=self)
|
||||
|
||||
# Project: Boolean
|
||||
def newbool(self, value):
|
||||
return W_Boolean(value, space=self)
|
||||
|
||||
def definemethod(self, name, code, w_target):
|
||||
w_meth = W_Method(code, name=name,
|
||||
slots={'__parent__': w_target},
|
||||
|
@ -3,25 +3,32 @@ all_primitives = []
|
||||
primitive_number_of_arguments = []
|
||||
|
||||
|
||||
def primitive(name, unwrap_spec, wrap_spec):
|
||||
def primitive(name, unwrap_spec, wrap_spec): # decorator arguments
|
||||
assert '$' + name not in registry, '${name} already defined'.format(name=name)
|
||||
primitive_number_of_arguments.append(len(unwrap_spec) - 1) # first argument is the receiver
|
||||
|
||||
def expose(func):
|
||||
def expose(func): # decorator
|
||||
def unwrapper(w_receiver, args_w, space):
|
||||
args = [w_receiver] + args_w
|
||||
if len(args) != len(unwrap_spec):
|
||||
if len(args) != len(unwrap_spec): # check that call args match primitive args
|
||||
raise TypeError(
|
||||
"Expected {ex} arguments, received {re}.".format(ex=len(unwrap_spec), re=len(args)))
|
||||
|
||||
unwrapped_args = ()
|
||||
for t, arg in zip(unwrap_spec, args):
|
||||
for t, arg in zip(unwrap_spec, args): # unpack values from simple-objects
|
||||
if t is int:
|
||||
unwrapped_args += (arg.value,)
|
||||
elif t is bool: # Project: Boolean
|
||||
unwrapped_args += (bool(arg.value),) # isn't necessary because "1 or 0" is valid
|
||||
else:
|
||||
unwrapped_args += (arg,)
|
||||
result = func(*unwrapped_args)
|
||||
if wrap_spec is int:
|
||||
|
||||
result = func(*unwrapped_args) # actual call
|
||||
|
||||
if wrap_spec is int: # wrap the result
|
||||
return space.newint(result)
|
||||
elif wrap_spec is bool: # Project: Boolean
|
||||
return space.newbool(result)
|
||||
return result
|
||||
|
||||
unwrapper.__qualname__ = name
|
||||
@ -45,12 +52,7 @@ def simple_int_add(a, b):
|
||||
return a + b
|
||||
|
||||
|
||||
@primitive('int_eq', [int, int], int)
|
||||
def simple_int_eq(a, b):
|
||||
return a == b
|
||||
|
||||
|
||||
# Syntactic Sugar Primitives
|
||||
# Project: Sugar
|
||||
@primitive("int_sub", [int, int], int)
|
||||
def simple_int_subtract(a, b):
|
||||
return a - b
|
||||
@ -74,3 +76,54 @@ def simple_int_modulo(a, b):
|
||||
@primitive("int_inc", [int], int)
|
||||
def simple_int_increment(a):
|
||||
return a + 1
|
||||
|
||||
|
||||
# Project: Boolean
|
||||
@primitive("bool_and", [bool, bool], bool)
|
||||
def simple_bool_and(a, b):
|
||||
return a and b
|
||||
|
||||
|
||||
@primitive("bool_or", [bool, bool], bool)
|
||||
def simple_bool_or(a, b):
|
||||
return a or b
|
||||
|
||||
|
||||
@primitive("bool_not", [bool], bool)
|
||||
def simple_bool_not(a):
|
||||
return not a
|
||||
|
||||
|
||||
@primitive("int_eq", [int, int], bool)
|
||||
def simple_int_eq(a, b):
|
||||
return a == b
|
||||
|
||||
|
||||
@primitive("int_leq", [int, int], bool)
|
||||
def simple_int_leq(a, b):
|
||||
return a <= b
|
||||
|
||||
|
||||
@primitive("int_geq", [int, int], bool)
|
||||
def simple_int_geq(a, b):
|
||||
return a >= b
|
||||
|
||||
|
||||
@primitive("int_greater", [int, int], bool)
|
||||
def simple_int_greater(a, b):
|
||||
return a > b
|
||||
|
||||
|
||||
@primitive("int_less", [int, int], bool)
|
||||
def simple_int_less(a, b):
|
||||
return a < b
|
||||
|
||||
|
||||
@primitive("int_tobool", [int], bool)
|
||||
def simple_int_tobool(a):
|
||||
return a
|
||||
|
||||
|
||||
@primitive("bool_toint", [bool], int)
|
||||
def simple_bool_toint(a):
|
||||
return a
|
||||
|
@ -93,8 +93,9 @@ class IntLiteral(Expression):
|
||||
self.value = int(value)
|
||||
|
||||
|
||||
# Project: String
|
||||
class StringLiteral(Expression):
|
||||
""" An string literal (like "hello world") """
|
||||
""" A string literal (like "hello world") """
|
||||
|
||||
attrs = ["value"]
|
||||
|
||||
@ -102,6 +103,16 @@ class StringLiteral(Expression):
|
||||
self.value = str(value)
|
||||
|
||||
|
||||
# Project: Boolean
|
||||
class BooleanLiteral(Expression):
|
||||
""" A boolean literal (like "false") """
|
||||
|
||||
attrs = ["value"]
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value == "true"
|
||||
|
||||
|
||||
class MethodCall(Expression):
|
||||
""" A call to a method with name 'methodname' on 'receiver' with
|
||||
'arguments' (which is a list of expression ASTs).
|
||||
|
@ -130,7 +130,6 @@ def make_single_string(delim):
|
||||
# Literals
|
||||
|
||||
Number = r'(([+-])?[1-9][0-9]*)|0'
|
||||
String = group(make_single_string(r"\'"), make_single_string(r'\"'))
|
||||
|
||||
# ____________________________________________________________
|
||||
# Ignored
|
||||
@ -159,7 +158,13 @@ OpenBracket = r'[\[\(\{]'
|
||||
CloseBracket = r'[\]\)\}]'
|
||||
|
||||
# ____________________________________________________________
|
||||
# Syntactic Sugar Operators
|
||||
# Project: Boolean, String
|
||||
|
||||
Boolean = group(r'true', r'false')
|
||||
String = group(make_single_string(r"\'"), make_single_string(r'\"'))
|
||||
|
||||
# ____________________________________________________________
|
||||
# Project: Sugar
|
||||
|
||||
Plus = r'\+'
|
||||
Minus = r'-'
|
||||
@ -178,8 +183,9 @@ Def = r'def'
|
||||
Object = r'object'
|
||||
|
||||
tokens = ["If", "Else", "While", "Def", "Object", "Number", "String", "Ignore",
|
||||
"Boolean", # Project: Boolean
|
||||
"NewlineAndWhitespace", "OpenBracket", "CloseBracket", "Comma", "Assign", "Colon",
|
||||
"Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Sugar
|
||||
"Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Project: Sugar
|
||||
"Name", "PrimitiveName"]
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@ from simplelexer import lex
|
||||
import simpleast
|
||||
|
||||
pg = ParserGenerator(["If", "Else", "While", "Def", "Object", "Number",
|
||||
"Boolean", # New Types
|
||||
"String", "Name", "Indent", "Dedent", "Newline", "OpenBracket",
|
||||
"CloseBracket", "Comma", "Assign", "Colon",
|
||||
"Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Sugar
|
||||
@ -154,7 +155,7 @@ def expression(expr):
|
||||
return expr[0]
|
||||
|
||||
|
||||
# Syntactic Sugar: Plus, Minus, Multiply, Divide, Modulo, Increment
|
||||
# Project: Sugar
|
||||
@pg.production("expression : expression Plus expression")
|
||||
@pg.production("expression : expression Minus expression")
|
||||
@pg.production("expression : expression Multiply expression")
|
||||
@ -197,9 +198,16 @@ def number_expression(stmt):
|
||||
return simpleast.IntLiteral(stmt[0].value)
|
||||
|
||||
|
||||
# Project: String
|
||||
@pg.production("basic_expression : String")
|
||||
def string_expression(stmt):
|
||||
return simpleast.StringLiteral(stmt[0].value)
|
||||
return simpleast.StringLiteral(stmt[0].value[1:-1]) # cut off delimiters
|
||||
|
||||
|
||||
# Project: Boolean
|
||||
@pg.production("basic_expression : Boolean")
|
||||
def boolean_expression(stmt):
|
||||
return simpleast.BooleanLiteral(stmt[0].value)
|
||||
|
||||
|
||||
@pg.production("basic_expression : implicitselfmethodcall")
|
||||
|
@ -1,10 +1,24 @@
|
||||
import py
|
||||
from rply import Token
|
||||
|
||||
from simpleast import Program, ExprStatement, BooleanLiteral, Assignment, ImplicitSelf
|
||||
from simplelexer import lex
|
||||
from simpleparser import parse
|
||||
from objmodel import W_NormalObject, W_Integer
|
||||
from objmodel import W_NormalObject, W_Integer, W_Boolean
|
||||
from interpreter import Interpreter
|
||||
|
||||
|
||||
def test_basic_boolean_lexing():
|
||||
assert lex("true")[0] == Token("Boolean", "true")
|
||||
assert lex("false")[0] == Token("Boolean", "false")
|
||||
assert lex("x = true")[:3] == [Token("Name", "x"), Token("Assign", "="), Token("Boolean", "true")]
|
||||
|
||||
|
||||
def test_basic_boolean_parsing():
|
||||
assert parse("false") == Program([ExprStatement(BooleanLiteral("false"))])
|
||||
assert parse("x = false") == Program([Assignment(ImplicitSelf(), "x", BooleanLiteral("false"))])
|
||||
|
||||
|
||||
def test_boolean_assignment():
|
||||
ast = parse("""
|
||||
x = true
|
||||
@ -24,7 +38,7 @@ def test_boolean_operations():
|
||||
ast = parse("""
|
||||
x = true and(false)
|
||||
y = true or(false)
|
||||
z = not(true)
|
||||
z = true not
|
||||
""")
|
||||
interpreter = Interpreter()
|
||||
w_model = interpreter.make_module()
|
||||
@ -55,7 +69,7 @@ y = 1 leq(2)
|
||||
|
||||
def test_boolean_conversion():
|
||||
ast = parse("""
|
||||
x = 1 toboolean
|
||||
x = 1 tobool
|
||||
y = true toint
|
||||
z = false toint
|
||||
""")
|
||||
|
Reference in New Issue
Block a user