Welcome to the next part of creating your own programming language. In this part, we’ll continue improving our toy language by implementing inheritance for the classes introduced earlier. Here are the previous parts: Building Your Own Programming Language From Scratch Building Your Own Programming Language From Scratch: Part II - Dijkstra's Two-Stack Algorithm Build Your Own Programming Language Part III: Improving Lexical Analysis with Regex Lookaheads Building Your Own Programming Language From Scratch Part IV: Implementing Functions Building Your Own Programming Language From Scratch: Part V - Arrays Building Your Own Programming Language From Scratch: Part VI - Loops Building Your Own Programming Language From Scratch: Part VII - Classes Building Your Own Programming Language From Scratch: Part VIII - Nested Classes The full source code is available . over on GitHub 1. Inheritance Model Let's start by defining the inheritance rules for our classes: To inherit a class, we’ll be using the colon character similar to C++ syntax: : class Base end class Derived: Base end We should be able to fill the class’s properties with the class’s properties: Base Derived class Base [base_arg] end class Derived [derived_arg1, derived_arg2]: Base [derived_arg2] end When we create an instance of the class containing constructor statements, the statements declared in the class’s constructor should be executed first: Derived Base class Base print "Constructor of Base class called" end class Derived: Base print "Constructor of Derived class called" end d = new Derived Output: Constructor of Base class called Constructor of Derived class called Our classes will support hybrid inheritance and derive a class from types: multiple class A end class B end class C end class Derived: A, B, C end To make an or operation for a class instance, we will use operator: Upcasting Downcasting as class Base end class Derived: Base end d = new Derived b = d as Base # Downcasting to Base d2 = b as Derived # Upcasting to Derived In order to modify the class’s properties, we should first downcast an object to the type: Base Base class Base [base_arg] end class Derived [derived_arg]: Base [derived_arg] end d = new Derived [ 1 ] # Directly changing a property of the Derived type d :: derived_arg = 2 # Downcasting instance to the Base and then changing Base’s property d as Base :: base_arg = 3 If we change a property in the class, the corresponding reference property in the class should be updated as well, and vice versa, if we change the class’s property that we used to construct the Base class, the corresponding property in this class should also be updated: Base Derived Derived Base class Base [base_arg] end class Derived [derived_arg]: Base [derived_arg] end d = new Derived [ 1 ] d as Base :: base_arg = 2 print d d :: derived_arg = 3 print d as Base Output: Derived [ derived_arg = 2 ] Base [ base_arg = 3 ] We won’t be using the keyword that we have in Java because, with hybrid inheritance, there could be inherited Base classes, and there is no way to know which class to refer to without defining it explicitly. super multiple super For this kind of action, we will use the cast operator to point the required type: Base class A fun action print "A action" end end class B fun action print "B action" end end class C: A, B fun action this as B :: action [] this as A :: action [] print "C action" end end c = new C c :: action [] Output: B action A action C action Lastly, in this part, we’ll add operator to check whether an object is an instance of a particular class or not: is class A end class B: A end fun check_instance [object] if object is B print "Object is type of B" elif object is A print "Object is type of A" end end check_instance [ new A ] check_instance [ new B ] Output: Object is type of A Object is type of B Now with these defined rules, let’s implement the inheritance model using already created structures from the previous parts. There are two main sections we’ll cover as usual: and . lexical analysis syntax analysis 2. Lexical Analysis In this section, we will cover lexical analysis. It’s a process to divide the source code into language lexemes, such as keywords, variables, operators, etc. To define the lexemes, I’m using the regex expressions listed in the enum. TokenType package org.example.toylanguage.token; public enum TokenType { Comment("\\#.*"), LineBreak("[\\n\\r]"), Whitespace("[\\s\\t]"), Keyword("(if|elif|else|end|print|input|class|fun|return|loop|in|by|break|next)(?=\\s|$)(?!_)"), GroupDivider("(\\[|\\]|\\,|\\{|}|\\.{2})"), Logical("(true|false)(?=\\s|$)(?!_)"), Numeric("([-]?(?=[.]?[0-9])[0-9]*(?![.]{2})[.]?[0-9]*)"), Null("(null)(?=,|\\s|$)(?!_)"), This("(this)(?=,|\\s|$)(?!_)"), Text("\"([^\"]*)\""), Operator("(\\+|-|\\*|/{1,2}|%|>=|>|<=|<{1,2}|={1,2}|!=|!|:{2}\\s+new|:{2}|\\(|\\)|(new|and|or)(?=\\s|$))(?!_)"), Variable("[a-zA-Z_]+[a-zA-Z0-9_]*"); private final String regex; } Every line of the toy language's source code is being processed through these regex expressions; with their help, we can the list of lexemes. accumulate Let’s add the missing lexemes required for our inheritance model: First, we need to add the single colon character to signify the class inheritance. We can add it to the lexeme with the 2 backslashes before the colon to make sure it will not be treated as a special character: : GroupDivider GroupDivider("(\\[|\\]|\\,|\\{|}|\\.{2}|\\:)") Next, we should check if we have the colon expression among already defined lexemes. And indeed, we have the lexeme with the double colon character to signify access to a class’s property or a function. We need to make this double colon NOT be treated as the lexeme with a single colon two times. Operator :: :: GroupDivider : The most reliable solution is to put a negative lookahead after the single colon expression saying that there shouldn't be a second colon after it: (?!\\:) GroupDivider("(\\[|\\]|\\,|\\{|}|\\.{2}|(\\:(?!\\:)))") To support Upcasting and Downcasting, we need to add operator to the lexeme: as Operator Operator("(\\+|-|\\*|/{1,2}|%|>=|>|<=|<{1,2}|={1,2}|!=|!|:{2}\\s+new|:{2}|\\(|\\)|(new|and|or|as)(?=\\s|$)(?!_))") Lastly, we put as a type check (instance of) operator to the lexeme as well: is Operator Operator("(\\+|-|\\*|/{1,2}|%|>=|>|<=|<{1,2}|={1,2}|!=|!|:{2}\\s+new|:{2}|\\(|\\)|(new|and|or|as|is)(?=\\s|$)(?!_))") We’ve added all the required regex expressions to the . These changes will be managed by , which will convert source code into tokens and handle them in the following section. TokenType LexicalParser 3. Syntax Analysis In this section, we will convert lexemes received from the lexical analyzer into the final statements following our language rules. 3.1 Class Declaration Currently, we use the to read and transform lexemes into definitions and statements. To parse a class definition, we use the method. StatementParser StatementParser#parseClassDefinition() All we are doing here is reading the class name and its arguments within square brackets, and at the end, we build ClassDefinition: private void parseClassDefinition() { // read class definition Token name = tokens.next(TokenType.Variable); List<String> properties = new ArrayList<>(); if (tokens.peek(TokenType.GroupDivider, "[")) { tokens.next(); //skip open square bracket while (!tokens.peek(TokenType.GroupDivider, "]")) { Token propertyToken = tokens.next(TokenType.Variable); properties.add(propertyToken.getValue()); if (tokens.peek(TokenType.GroupDivider, ",")) tokens.next(); } tokens.next(TokenType.GroupDivider, "]"); //skip close square bracket } // read base types ... // add class definition ... ClassDefinition classDefinition = new ClassDefinition(name, properties, …); // parse constructor statements ... tokens.next(TokenType.Keyword, "end"); } For the class, we should read the inherited types and corresponding reference properties. Derived class’s properties and the derived class’s properties can differ, and to store a relation between these properties, we’ll introduce an auxiliary class that will contain the class name and its properties: The base package org.example.toylanguage.context.definition; @RequiredArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class ClassDetails { @EqualsAndHashCode.Include private final String name; private final List<String> properties; } package org.example.toylanguage.context.definition; @RequiredArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class ClassDefinition implements Definition { @EqualsAndHashCode.Include private final ClassDetails classDetails; private final Set<ClassDetails> baseTypes; private final ClassStatement statement; private final DefinitionScope definitionScope; } Now, let’s fill the inherited classes for our . We will use to keep the order of types, which we’ll be using to initialize super constructors in the order defined by a developer: ClassDefinition LinkedHashSet Base private void parseClassDefinition() { // read class definition ... // read base types Set<ClassDetails> baseTypes = new LinkedHashSet<>(); if (tokens.peek(TokenType.GroupDivider, ":")) { while (tokens.peek(TokenType.GroupDivider, ":", ",")) { tokens.next(); Token baseClassName = tokens.next(TokenType.Variable); List<String> baseClassProperties = new ArrayList<>(); if (tokens.peek(TokenType.GroupDivider, "[")) { tokens.next(); //skip open square bracket while (!tokens.peek(TokenType.GroupDivider, "]")) { Token baseClassProperty = tokens.next(TokenType.Variable); baseClassProperties.add(baseClassProperty.getValue()); if (tokens.peek(TokenType.GroupDivider, ",")) tokens.next(); } tokens.next(TokenType.GroupDivider, "]"); //skip close square bracket } ClassDetails baseClassDetails = new ClassDetails(baseClassName.getValue(), baseClassProperties); baseTypes.add(baseClassDetails); } } // add class definition ... ClassDetails classDetails = new ClassDetails(name.getValue(), properties); ClassDefinition classDefinition = new ClassDefinition(classDetails, baseTypes, classStatement, classScope); // parse constructor statements ... tokens.next(TokenType.Keyword, "end"); } 3.2 Class Instance After we’ve read and saved the class definition, we should provide the ability to create an instance with the defined inherited types. Currently, to create an instance of a class, we use the and implementations. Derived ClassExpression ClassValue The first one, the ClassExpression, is being used to create a class instance in the . The second one, the ClassValue, is being created by the during execution and used to access a class property. ExpressionReader ClassExpression Let's begin with the second, ClassValue. When we create a class instance and invoke its constructor statements, we may need to access its properties. Each type can have a different set of properties that do not necessarily correspond to the type’s properties. Derived Base As the next requirement of our inheritance rules, where we need to provide the cast operator to the type, we should make an interface to easily switch between types and keep variables of each class isolated from variables of the type. Base Base Base Derived We'll define the Map of relations for the types and the type to access the by class name: Base Derived ClassValue @Getter public class ClassValue extends IterableValue<ClassDefinition> { private final MemoryScope memoryScope; private final Map<String, ClassValue> relations; public ClassValue(ClassDefinition definition, MemoryScope memoryScope, Map<String, ClassValue> relations) { super(definition); this.memoryScope = memoryScope; this.relations = relations; } public ClassValue getRelation(String className) { return relations.get(className); } public boolean containsRelation(String className) { return relations.containsKey(className); } ... } These relations will be used for Upcasting, Downcasting, and checking object type. To simplify the process of Upcasting and Downcasting, this map will contain the same values for each Base class in the inheritance chain and will allow Upcasting from the type to the type and vice versa. bottom Base top Derived Next, let’s modify the ClassExpression that we use to create a class instance. Inside it, we define the Map of relations a second time, which will be used to create a ClassValue and be accumulated by every supertype the class has: Derived @RequiredArgsConstructor @Getter public class ClassExpression implements Expression { private final String name; private final List<? extends Expression> argumentExpressions; // Base classes and Derived class available to a class instance private final Map<String, ClassValue> relations; public ClassExpression(String name, List<Expression> argumentExpressions) { this(name, argumentExpressions, new HashMap<>()); } } We should also provide consistency for reference properties. If we modify the type’s property, the reference property in the type should also be updated to the same value, and vice versa, if we change the class’s property, the reference property in the class should be updated as well: Base Derived Derived Base class A [arg_a] end class B [arg_b1, arg_b2]: A [arg_b2] end b = new B [ 1, 2 ] b as A :: arg_a = 3 print b b :: arg_b2 = 4 print b as A Output B [ arg_b1 = 1, arg_b2 = 3 ] A [ arg_a = 4 ] This reference consistency can be delegated to Java by introducing a new ValueReference wrapper for the Value, a single instance of which will be used by the Derived type to initialize the reference property in the Base types: package org.example.toylanguage.context; /** * Wrapper for the Value to keep the properties relation between a Base class and a Derived class * * <pre>{@code * # Declare the Base class A * class A [a_value] * end * * # Declare the Derived class B that inherits class A and initializes its `a_value` property with the `b_value` parameter * class B [b_value]: A [b_value] * end * * # Create an instance of class B * b = new B [ b_value ] * * # If we change the `b_value` property, the A class's property `a_value` should be updated as well * b :: b_value = new_value * * # a_new_value should contain `new_value` * a_new_value = b as A :: a_value * }</pre> */ @Getter @Setter public class ValueReference implements Expression { private Value<?> value; private ValueReference(Value<?> value) { this.value = value; } public static ValueReference instanceOf(Expression expression) { if (expression instanceof ValueReference) { // reuse variable return (ValueReference) expression; } else { return new ValueReference(expression.evaluate()); } } @Override public Value<?> evaluate() { return value; } } Now, let’s initialize the classes’ constructors inside the method. This method accepts a list of properties to instantiate the regular classes and the nested ones: Base ClassExpression#evaluate(List<Value<?>>) private Value<?> evaluate(List<Value<?>> values) { //get class's definition and statement ClassDefinition definition = DefinitionContext.getScope().getClass(name); ClassStatement classStatement = definition.getStatement(); //set separate scope MemoryScope classScope = new MemoryScope(null); MemoryContext.pushScope(classScope); try { //initialize constructor arguments ClassValue classValue = new ClassValue(definition, classScope); ClassInstanceContext.pushValue(classValue); IntStream.range(0, definition.getProperties().size()).boxed() .forEach(i -> MemoryContext.getScope() .setLocal(definition.getProperties().get(i), values.size() > i ? values.get(i) : NullValue.NULL_INSTANCE)); //execute function body DefinitionContext.pushScope(definition.getDefinitionScope()); try { classStatement.execute(); } finally { DefinitionContext.endScope(); } return classValue; } finally { MemoryContext.endScope(); ClassInstanceContext.popValue(); } } We will modify the part after we set a separate . First, we need to create an instance of ClassValue, and add it to the relations map: MemoryScope //set separate scope MemoryScope classScope = new MemoryScope(null); MemoryContext.pushScope(classScope); //initialize constructor arguments ClassValue classValue = new ClassValue(definition, classScope, relations); relations.put(name, classValue); Next, we will convert the Value<?> properties into the ValueReference: List<ValueReference> valueReferences = values.stream() .map(ValueReference::instanceOf) .collect(Collectors.toList()); If we instantiate the class’s reference property using , the second time this expression will return the same ValueReference. Derived ValueReference#instanceOf(Expression) After that, we can fill in the missing arguments in case a developer does not provide enough properties defined in the class definition. These absent properties can be set with the NullValue: // fill the missing properties with NullValue.NULL_INSTANCE // class A [arg1, arg2] // new A [arg1] -> new A [arg1, null] // new A [arg1, arg2, arg3] -> new A [arg1, arg2] List<ValueReference> valuesToSet = IntStream.range(0, definition.getClassDetails().getProperties().size()) .boxed() .map(i -> values.size() > i ? values.get(i) : ValueReference.instanceOf(NullValue.NULL_INSTANCE)) .collect(Collectors.toList()); Lastly, for this method, we need to create a ClassExpression for each class using the class’s reference properties, and then execute each constructor by calling : Base Derived ClassExpression#evaluate() //invoke constructors of the base classes definition.getBaseTypes() .stream() .map(baseType -> { // initialize base class's properties // class A [a_arg] // class B [b_arg1, b_arg2]: A [b_arg1] List<ValueReference> baseClassProperties = baseType.getProperties().stream() .map(t -> definition.getClassDetails().getProperties().indexOf(t)) .map(valuesToSet::get) .collect(Collectors.toList()); return new ClassExpression(baseType.getName(), baseClassProperties, relations); }) .forEach(ClassExpression::evaluate); With this block of code, we can instantiate each class we have in the inheritance chain. When we create an instance of the ClassExpression for the Base class, this class will behave like a class, with its own inherited Base types, until we reach the Base class that does not inherit any classes. Base Base Derived After we initialized the Base instances, we can finish initializing the Derived instance by setting its properties with and executing constructor statements: MemoryScope#setLocal(ValueReference) try { ClassInstanceContext.pushValue(classValue); IntStream.range(0, definition.getClassDetails().getArguments().size()).boxed() .forEach(i -> MemoryContext.getScope() .setLocal(definition.getClassDetails().getArguments().get(i), valuesToSet.get(i))); //execute constructor statements DefinitionContext.pushScope(definition.getDefinitionScope()); try { classStatement.execute(); } finally { DefinitionContext.endScope(); } return classValue; } finally { MemoryContext.endScope(); ClassInstanceContext.popValue(); } With the new ValueReference class as a value wrapper, we need also to update the MemoryScope to be able to set a ValueReference directly and update the Value<?> inside it if we modify the class’ property: public class MemoryScope { private final Map<String, ValueReference> variables; private final MemoryScope parent; public MemoryScope(MemoryScope parent) { this.variables = new HashMap<>(); this.parent = parent; } public Value<?> get(String name) { ValueReference variable = variables.get(name); if (variable != null) return variable.getValue(); else if (parent != null) return parent.get(name); else return NullValue.NULL_INSTANCE; } public Value<?> getLocal(String name) { ValueReference variable = variables.get(name); return variable != null ? variable.getValue() : null; } public void set(String name, Value<?> value) { MemoryScope variableScope = findScope(name); if (variableScope == null) { setLocal(name, value); } else { variableScope.setLocal(name, value); } } // set variable as a reference public void setLocal(String name, ValueReference variable) { variables.put(name, variable); } // update an existent variable public void setLocal(String name, Value<?> value) { if (variables.containsKey(name)) { variables.get(name).setValue(value); } else { variables.put(name, ValueReference.instanceOf(value)); } } private MemoryScope findScope(String name) { if (variables.containsKey(name)) return this; return parent == null ? null : parent.findScope(name); } } 3.3 Function This subsection will cover the function invocation in the inheritance model. Currently, to invoke a function, we use the FunctionExpression class. We’re only interested in the method, the one that accepts the ClassValue as a type, which we use to execute a function from: FunctionExpression#evaluate(ClassValue) /** * Evaluate class's function * * @param classValue instance of class where the function is placed in */ public Value<?> evaluate(ClassValue classValue) { //initialize function arguments List<Value<?>> values = argumentExpressions.stream().map(Expression::evaluate).collect(Collectors.toList()); //get definition and memory scopes from class definition ClassDefinition classDefinition = classValue.getValue(); DefinitionScope classDefinitionScope = classDefinition.getDefinitionScope(); MemoryScope memoryScope = classValue.getMemoryScope(); //set class's definition and memory scopes DefinitionContext.pushScope(classDefinitionScope); MemoryContext.pushScope(memoryScope); ClassInstanceContext.pushValue(classValue); try { //proceed function return evaluate(values); } finally { DefinitionContext.endScope(); MemoryContext.endScope(); ClassInstanceContext.popValue(); } } With the inheritance, we may not have a function declared in the class. This function could be available only in one of the classes. In the following example, the function is only available in the definition of the B class. Derived Base action class A end class B fun action end end class C: A, B end c = new C c :: action [] To find the class containing the function with name and number of arguments, we will create the following method: Base private ClassDefinition findClassDefinitionContainingFunction(ClassDefinition classDefinition, String functionName, int argumentsSize) { DefinitionScope definitionScope = classDefinition.getDefinitionScope(); if (definitionScope.containsFunction(functionName, argumentsSize)) { return classDefinition; } else { for (ClassDetails baseType : classDefinition.getBaseTypes()) { ClassDefinition baseTypeDefinition = definitionScope.getClass(baseType.getName()); ClassDefinition functionClassDefinition = findClassDefinitionContainingFunction(baseTypeDefinition, functionName, argumentsSize); if (functionClassDefinition != null) return functionClassDefinition; } return null; } } With this method and with the earlier defined , we can retrieve the ClassValue instance that we can use to invoke the function. Let’s finish the implementation: ClassValue#getRelation(String) FunctionExpression#evaluate(ClassValue) /** * Evaluate class's function * * @param classValue instance of class where the function is placed in */ public Value<?> evaluate(ClassValue classValue) { //initialize function arguments List<Value<?>> values = argumentExpressions.stream().map(Expression::evaluate).collect(Collectors.toList()); // find a class containing the function ClassDefinition classDefinition = findClassDefinitionForFunction(classValue.getValue(), name, values.size()); if (classDefinition == null) { throw new ExecutionException(String.format("Function is not defined: %s", name)); } DefinitionScope classDefinitionScope = classDefinition.getDefinitionScope(); ClassValue functionClassValue = classValue.getRelation(classDefinition.getClassDetails().getName()); MemoryScope memoryScope = functionClassValue.getMemoryScope(); //set class's definition and memory scopes DefinitionContext.pushScope(classDefinitionScope); MemoryContext.pushScope(memoryScope); ClassInstanceContext.pushValue(functionClassValue); try { //proceed function return evaluate(values); } finally { DefinitionContext.endScope(); MemoryContext.endScope(); ClassInstanceContext.popValue(); } } 3.4 Cast Type Operator In this subsection, we will add support for the cast type operator. We already defined this expression in the lexeme. as TokenType.Operator We need only to create the implementation that will transform the initial ClassValue into the Base or Derived type using the map: BinaryOperatorExpression ClassValue#relations package org.example.toylanguage.expression.operator; /** * Cast a class instance from one type to other */ public class ClassCastOperator extends BinaryOperatorExpression { public ClassCastOperator(Expression left, Expression right) { super(left, right); } @Override public Value<?> evaluate() { // evaluate expressions ClassValue classInstance = (ClassValue) getLeft().evaluate(); String typeToCastName = ((VariableExpression) getRight()).getName(); // retrieve class details ClassDetails classDetails = classInstance.getValue().getClassDetails(); // check if the type to cast is different from original if (classDetails.getName().equals(typeToCastName)) { return classInstance; } else { // retrieve ClassValue of other type return classInstance.getRelation(typeToCastName); } } } And as the last step, we should plug this operator in the enum with the required precedence for this operation: Operator @RequiredArgsConstructor @Getter public enum Operator { Not("!", NotOperator.class, 7), ClassInstance("new", ClassInstanceOperator.class, 7), NestedClassInstance(":{2}\\s+new", NestedClassInstanceOperator.class, 7), ClassProperty(":{2}", ClassPropertyOperator.class, 7), ClassCast("as", ClassCastOperator.class, 7), ... private final String character; private final Class<? extends OperatorExpression> type; private final Integer precedence; ... } This enum is also built with the regex model and will transform the list of lexemes into the operators’ implementations. The provided precedence will be taken into account with the help of Dijkstra's Two-Stack Algorithm. Please check out the and for more explanation. ExpressionReader implementation the second part 3.5 Check Type Operator In this last subsection of syntax analysis, we’ll define the check type operator. The implementation will be similar to the cast operator, which requires creating the implementation and plugging it into the Operator enum. OperatorExpression The check type operator should return , which stands for a boolean type containing either true or false: LogicalValue package org.example.toylanguage.expression.operator; import org.example.toylanguage.exception.ExecutionException; import org.example.toylanguage.expression.Expression; import org.example.toylanguage.expression.VariableExpression; import org.example.toylanguage.expression.value.ClassValue; import org.example.toylanguage.expression.value.LogicalValue; import org.example.toylanguage.expression.value.Value; public class ClassInstanceOfOperator extends BinaryOperatorExpression { public ClassInstanceOfOperator(Expression left, Expression right) { super(left, right); } @Override public Value<?> evaluate() { Value<?> left = getLeft().evaluate(); // cat = new Cat // is_cat_animal = cat is Animal if (left instanceof ClassValue && getRight() instanceof VariableExpression) { String classType = ((VariableExpression) getRight()).getName(); return new LogicalValue(((ClassValue) left).containsRelation(classType)); } else { throw new ExecutionException(String.format("Unable to perform `is` operator for the following operands `%s` and `%s`", left, getRight())); } } } @RequiredArgsConstructor @Getter public enum Operator { Not("!", NotOperator.class, 7), ClassInstance("new", ClassInstanceOperator.class, 7), NestedClassInstance(":{2}\\s+new", NestedClassInstanceOperator.class, 7), ClassProperty(":{2}", ClassPropertyOperator.class, 7), ClassCast("as", ClassCastOperator.class, 7), ClassInstanceOf("is", ClassInstanceOfOperator.class, 7), ... private final String character; private final Class<? extends OperatorExpression> type; private final Integer precedence; ... } You can create your own operators the same way, by defining the regex expression in the lexeme and plugging the implementation in the enum. TokenType.Operator OperatorExpression Operator 4 Wrap Up That’s all the modifications we needed to make to implement the inheritance. In this part, we created a simple hybrid inheritance model as one more step toward making a complete programming language. Here are a few examples you can run and test on your own with : RunToyLanguage class Animal fun action print "Animals can run." end end class Bird fun action print "Birds can fly." end end class Parrot: Animal, Bird fun action this as Bird :: action [] this as Animal :: action [] print "Parrots can talk." end end new Parrot :: action[] class Add [x, y] fun sum return "The sum of " + x + " and " + y + " is " + (x + y) end end class Mul [a, b] fun mul return "The multiplication of " + a + " and " + b + " is " + a * b end end class Sub [a, b] fun sub return "The subtraction of " + a + " and " + b + " is " + (a - b) end end class Div [m, n] fun div return "The division of " + m + " and " + n + " is " + m / n end end class Exp [m, n] fun exp return "The exponentiation of " + m + " and " + n + " is " + m ** n end end class Fib [ n ] fun fib return "The fibonacci number for " + n + " is " + fib [ n ] end fun fib [ n ] if n < 2 return n end return fib [ n - 1 ] + fib [ n - 2 ] end end class Calculator [p, q]: Add [p, q], Sub [q, p], Mul [p, q], Div [q, p], Exp [p, q], Fib [ q ] end calc = new Calculator [2, 10] print calc :: sum [] print calc :: sub [] print calc :: mul [] print calc :: div [] print calc :: exp [] print calc :: fib [] Photo by on Uday Awal Unsplash