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:
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

View File

@ -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)

View File

@ -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)

View File

@ -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):

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_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},

View File

@ -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

View File

@ -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).

View File

@ -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"]

View File

@ -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")

View File

@ -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
""")