1

implement boolean

This commit is contained in:
Christoph
2021-08-13 12:00:24 +02:00
parent c91f3f89e7
commit 8b70e97601
10 changed files with 200 additions and 52 deletions

View File

@ -7,5 +7,42 @@ def pass:
object inttrait: object inttrait:
def add(other): def add(other):
self $int_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): def eq(other):
self $int_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

View File

@ -64,6 +64,9 @@ class Interpreter(object):
elif opcode == compile.INT_LITERAL: elif opcode == compile.INT_LITERAL:
w_value = self.space.newint(oparg) w_value = self.space.newint(oparg)
stack.append(w_value) 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: elif opcode == compile.MAKE_FUNCTION:
bc = bytecode.subbytecodes[oparg] bc = bytecode.subbytecodes[oparg]
w_method = self.space.definemethod(name=bc.name, code=bc, w_target=w_context) w_method = self.space.definemethod(name=bc.name, code=bc, w_target=w_context)

View File

@ -109,23 +109,24 @@ import simpleast
# ---------- bytecodes ---------- # ---------- bytecodes ----------
INT_LITERAL = 2 # integer value BOOL_LITERAL = 1 # 1 or 0 # Project: Boolean
ASSIGNMENT = 4 # index of attrname INT_LITERAL = 2 # integer value
METHOD_LOOKUP = 5 # index of method name ASSIGNMENT = 4 # index of attrname
METHOD_CALL = 6 # number of arguments METHOD_LOOKUP = 5 # index of method name
PRIMITIVE_METHOD_CALL = 7 # number of the primitive METHOD_CALL = 6 # number of arguments
MAKE_FUNCTION = 8 # bytecode literal index PRIMITIVE_METHOD_CALL = 7 # number of the primitive
MAKE_OBJECT = 9 # index of object name MAKE_FUNCTION = 8 # bytecode literal index
ASSIGNMENT_APPEND_PARENT = 10 # index of parentname MAKE_OBJECT = 9 # index of object name
MAKE_OBJECT_CALL = 11 # bytecode literal index ASSIGNMENT_APPEND_PARENT = 10 # index of parentname
JUMP_IF_FALSE = 12 # offset MAKE_OBJECT_CALL = 11 # bytecode literal index
JUMP = 13 # offset JUMP_IF_FALSE = 12 # offset
GET_LOCAL = 15 # index of attrname (optimization) JUMP = 13 # offset
SET_LOCAL = 16 # index of attrname (optimization) GET_LOCAL = 15 # index of attrname (optimization)
SET_LOCAL = 16 # index of attrname (optimization)
IMPLICIT_SELF = 32 # (no argument) IMPLICIT_SELF = 32 # (no argument)
POP = 33 # (no argument) POP = 33 # (no argument)
DUP = 34 # (no argument) DUP = 34 # (no argument)
opcode_names = [None] * 256 opcode_names = [None] * 256
for key, value in list(globals().items()): for key, value in list(globals().items()):
@ -133,11 +134,11 @@ for key, value in list(globals().items()):
opcode_names[value] = key opcode_names[value] = key
def hasarg(opcode): def hasarg(opcode):
""" Helper function to determine whether an opcode has an argument.""" """ Helper function to determine whether an opcode has an argument."""
return opcode < 32 return opcode < 32
def isjump(opcode): def isjump(opcode):
""" Helper function to determine whether an opcode is a jump.""" """ Helper function to determine whether an opcode is a jump."""
return opcode == JUMP_IF_FALSE or opcode == JUMP return opcode == JUMP_IF_FALSE or opcode == JUMP
@ -188,6 +189,7 @@ def compile(ast, argumentnames=[], name=None):
stack_effects = { stack_effects = {
BOOL_LITERAL: 1, # Project: Boolean
INT_LITERAL: 1, INT_LITERAL: 1,
ASSIGNMENT: -1, ASSIGNMENT: -1,
METHOD_LOOKUP: 1, METHOD_LOOKUP: 1,
@ -219,10 +221,10 @@ class Compiler(object):
for name, index in list(self.symbols.items()): for name, index in list(self.symbols.items()):
symbols[index] = name symbols[index] = name
result = Bytecode(''.join(self.code), result = Bytecode(''.join(self.code),
funcname, funcname,
symbols, symbols,
self.subbytecodes, self.subbytecodes,
numargs, self.max_stackdepth) numargs, self.max_stackdepth)
assert self.stackdepth == 1 assert self.stackdepth == 1
return result return result
@ -257,10 +259,10 @@ class Compiler(object):
return len(self.code) return len(self.code)
def set_target_position(self, oldposition, newtarget): def set_target_position(self, oldposition, newtarget):
offset = newtarget - (oldposition+5) offset = newtarget - (oldposition + 5)
i = 0 i = 0
for c in self.encode4(offset): for c in self.encode4(offset):
self.code[oldposition+1+i] = c self.code[oldposition + 1 + i] = c
i += 1 i += 1
def encode4(self, value): def encode4(self, value):
@ -274,13 +276,16 @@ class Compiler(object):
self.symbols[symbol] = len(self.symbols) self.symbols[symbol] = len(self.symbols)
return self.symbols[symbol] return self.symbols[symbol]
def compile(self, ast, needsresult=True): def compile(self, ast, needsresult=True):
return getattr(self, "compile_" + ast.__class__.__name__)(ast, needsresult) return getattr(self, "compile_" + ast.__class__.__name__)(ast, needsresult)
def compile_IntLiteral(self, astnode, needsresult): def compile_IntLiteral(self, astnode, needsresult):
self.emit(INT_LITERAL, astnode.value) 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): def compile_ImplicitSelf(self, astnode, needsresult):
self.emit(IMPLICIT_SELF) self.emit(IMPLICIT_SELF)
@ -318,7 +323,7 @@ class Compiler(object):
expected_args = primitives.get_number_of_arguments_of_primitive(index) expected_args = primitives.get_number_of_arguments_of_primitive(index)
if not (len(astnode.arguments) == expected_args): if not (len(astnode.arguments) == expected_args):
raise TypeError( 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) self.compile(astnode.receiver)
for arg in astnode.arguments: for arg in astnode.arguments:
self.compile(arg) self.compile(arg)

View File

@ -74,16 +74,17 @@ class W_NormalObject(AbstractObject):
class W_Integer(AbstractObject): class W_Integer(AbstractObject):
def __init__(self, value, space=None): def __init__(self, value, space=None, trait="inttrait"):
self.value = value self.value = int(value)
self.space = space self.space = space
self.__trait = trait # used this to extend from W_Integer
def getparents(self): def getparents(self):
if self.space is None: if self.space is None:
return [] # for tests return [] # for tests
inttrait = self.space.getbuiltin('inttrait') trait = self.space.getbuiltin(self.__trait)
assert inttrait is not None, 'O_o bogus state' assert trait is not None, 'O_o bogus state'
return [inttrait] return [trait]
def hasslot(self, name): def hasslot(self, name):
return False return False
@ -97,6 +98,12 @@ class W_Integer(AbstractObject):
return self.value != 0 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): class W_Method(W_NormalObject):
def __init__(self, code, *args, **kwargs): def __init__(self, code, *args, **kwargs):

View File

@ -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_Method
from objmodel import W_NormalObject from objmodel import W_NormalObject
@ -49,6 +49,10 @@ class ObjectSpace(object):
def newint(self, value): def newint(self, value):
return W_Integer(value, space=self) 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): def definemethod(self, name, code, w_target):
w_meth = W_Method(code, name=name, w_meth = W_Method(code, name=name,
slots={'__parent__': w_target}, slots={'__parent__': w_target},

View File

@ -3,25 +3,32 @@ all_primitives = []
primitive_number_of_arguments = [] 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) 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 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): def unwrapper(w_receiver, args_w, space):
args = [w_receiver] + args_w 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( raise TypeError(
"Expected {ex} arguments, received {re}.".format(ex=len(unwrap_spec), re=len(args))) "Expected {ex} arguments, received {re}.".format(ex=len(unwrap_spec), re=len(args)))
unwrapped_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: if t is int:
unwrapped_args += (arg.value,) 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: else:
unwrapped_args += (arg,) 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) return space.newint(result)
elif wrap_spec is bool: # Project: Boolean
return space.newbool(result)
return result return result
unwrapper.__qualname__ = name unwrapper.__qualname__ = name
@ -45,12 +52,7 @@ def simple_int_add(a, b):
return a + b return a + b
@primitive('int_eq', [int, int], int) # Project: Sugar
def simple_int_eq(a, b):
return a == b
# Syntactic Sugar Primitives
@primitive("int_sub", [int, int], int) @primitive("int_sub", [int, int], int)
def simple_int_subtract(a, b): def simple_int_subtract(a, b):
return a - b return a - b
@ -74,3 +76,54 @@ def simple_int_modulo(a, b):
@primitive("int_inc", [int], int) @primitive("int_inc", [int], int)
def simple_int_increment(a): def simple_int_increment(a):
return a + 1 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

View File

@ -93,8 +93,9 @@ class IntLiteral(Expression):
self.value = int(value) self.value = int(value)
# Project: String
class StringLiteral(Expression): class StringLiteral(Expression):
""" An string literal (like "hello world") """ """ A string literal (like "hello world") """
attrs = ["value"] attrs = ["value"]
@ -102,6 +103,16 @@ class StringLiteral(Expression):
self.value = str(value) 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): class MethodCall(Expression):
""" A call to a method with name 'methodname' on 'receiver' with """ A call to a method with name 'methodname' on 'receiver' with
'arguments' (which is a list of expression ASTs). 'arguments' (which is a list of expression ASTs).

View File

@ -130,7 +130,6 @@ def make_single_string(delim):
# Literals # Literals
Number = r'(([+-])?[1-9][0-9]*)|0' Number = r'(([+-])?[1-9][0-9]*)|0'
String = group(make_single_string(r"\'"), make_single_string(r'\"'))
# ____________________________________________________________ # ____________________________________________________________
# Ignored # Ignored
@ -159,7 +158,13 @@ OpenBracket = r'[\[\(\{]'
CloseBracket = 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'\+' Plus = r'\+'
Minus = r'-' Minus = r'-'
@ -178,8 +183,9 @@ Def = r'def'
Object = r'object' Object = r'object'
tokens = ["If", "Else", "While", "Def", "Object", "Number", "String", "Ignore", tokens = ["If", "Else", "While", "Def", "Object", "Number", "String", "Ignore",
"Boolean", # Project: Boolean
"NewlineAndWhitespace", "OpenBracket", "CloseBracket", "Comma", "Assign", "Colon", "NewlineAndWhitespace", "OpenBracket", "CloseBracket", "Comma", "Assign", "Colon",
"Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Sugar "Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Project: Sugar
"Name", "PrimitiveName"] "Name", "PrimitiveName"]

View File

@ -6,6 +6,7 @@ from simplelexer import lex
import simpleast import simpleast
pg = ParserGenerator(["If", "Else", "While", "Def", "Object", "Number", pg = ParserGenerator(["If", "Else", "While", "Def", "Object", "Number",
"Boolean", # New Types
"String", "Name", "Indent", "Dedent", "Newline", "OpenBracket", "String", "Name", "Indent", "Dedent", "Newline", "OpenBracket",
"CloseBracket", "Comma", "Assign", "Colon", "CloseBracket", "Comma", "Assign", "Colon",
"Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Sugar "Increment", "Plus", "Minus", "Multiply", "Divide", "Modulo", # Sugar
@ -154,7 +155,7 @@ def expression(expr):
return expr[0] return expr[0]
# Syntactic Sugar: Plus, Minus, Multiply, Divide, Modulo, Increment # Project: Sugar
@pg.production("expression : expression Plus expression") @pg.production("expression : expression Plus expression")
@pg.production("expression : expression Minus expression") @pg.production("expression : expression Minus expression")
@pg.production("expression : expression Multiply expression") @pg.production("expression : expression Multiply expression")
@ -197,9 +198,16 @@ def number_expression(stmt):
return simpleast.IntLiteral(stmt[0].value) return simpleast.IntLiteral(stmt[0].value)
# Project: String
@pg.production("basic_expression : String") @pg.production("basic_expression : String")
def string_expression(stmt): 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") @pg.production("basic_expression : implicitselfmethodcall")

View File

@ -1,10 +1,24 @@
import py import py
from rply import Token
from simpleast import Program, ExprStatement, BooleanLiteral, Assignment, ImplicitSelf
from simplelexer import lex
from simpleparser import parse from simpleparser import parse
from objmodel import W_NormalObject, W_Integer from objmodel import W_NormalObject, W_Integer, W_Boolean
from interpreter import Interpreter 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(): def test_boolean_assignment():
ast = parse(""" ast = parse("""
x = true x = true
@ -24,7 +38,7 @@ def test_boolean_operations():
ast = parse(""" ast = parse("""
x = true and(false) x = true and(false)
y = true or(false) y = true or(false)
z = not(true) z = true not
""") """)
interpreter = Interpreter() interpreter = Interpreter()
w_model = interpreter.make_module() w_model = interpreter.make_module()
@ -55,7 +69,7 @@ y = 1 leq(2)
def test_boolean_conversion(): def test_boolean_conversion():
ast = parse(""" ast = parse("""
x = 1 toboolean x = 1 tobool
y = true toint y = true toint
z = false toint z = false toint
""") """)