diff --git a/src/AST.kt b/src/AST.kt deleted file mode 100644 index 0b57071..0000000 --- a/src/AST.kt +++ /dev/null @@ -1,54 +0,0 @@ -enum class BinOpType { - Add, - Subtract, - Multiply, - Divide, - Modulus, - Lt, - Leq, - Gt, - Geq, - Equal, - Neq, - And, - Or -} - -enum class UnOpType { - Negate, - Not -} - -enum class BuiltInFunction { - OutNumber -} - -sealed class ASTNode { - data class Identifier(val name: String, val constant: Boolean) : ASTNode() - data class NumberLiteral(val value: Int) : ASTNode() - data class BooleanLiteral(val value: Boolean) : ASTNode() - data class Let(val identifier: ASTNode?, - val expr: ASTNode?) : ASTNode() - data class Assign(val identifier: ASTNode?, - val expr: ASTNode?) : ASTNode() - data class Semicolon(val left: ASTNode?, - val right: ASTNode?) : ASTNode() - data class Block(val expr: ASTNode?) : ASTNode() - data class IfElse(val conds: ArrayList, - val ifTrues: ArrayList, - val ifFalse: ASTNode?) : ASTNode() - data class While(val cond: ASTNode?, - val whileTrue: ASTNode?) : ASTNode() - data class FunctionDec(val params: ArrayList, - val expr: ASTNode?) : ASTNode() - data class FunctionCall(val function: ASTNode?, - val params: ArrayList) : ASTNode() - data class BuiltInCall(val builtIn: BuiltInFunction, - val params: ArrayList) : ASTNode() - data class BinOp(val binOp: BinOpType, - val left: ASTNode?, - val right: ASTNode?) : ASTNode() - data class UnOp(val unOp: UnOpType, - val expr: ASTNode?) : ASTNode() - -} \ No newline at end of file diff --git a/src/Environment.kt b/src/Environment.kt index cbdb8a0..82d50d3 100644 --- a/src/Environment.kt +++ b/src/Environment.kt @@ -1,41 +1,40 @@ +import ast.ASTNode + +data class ValueAndConstStatus(var value: ASTNode, val constant: Boolean) + class Environment(val parent: Environment?) { - data class ValueAndConstStatus(var value: Value, val constant: Boolean) val values = HashMap() - fun assignUp(name: String, value: Value) : Boolean { + fun assignUp(name: String, value: ASTNode): Boolean { if (values.contains(name)) { if (values[name]!!.constant) { throw Exception("Interpreter error: Cannot reassign constant $name") } values[name]?.value = value return true - } - else if (parent != null) { + } else if (parent != null) { return parent.assignUp(name, value) } return false } - fun assign(name: String, value: Value, constant: Boolean) { - if (values.contains(name)) { - if (values[name]!!.constant) { - throw Exception("Interpreter error: Cannot reassign constant $name") - } + fun assign(name: String, value: ASTNode, constant: Boolean) { + if (values.contains(name) && values[name]!!.constant) { + throw Exception("Interpreter error: Cannot reassign constant $name") } if (assignUp(name, value)) { return } - if(name in values) { + if (!values.contains(name)) { values[name] = ValueAndConstStatus(value, constant) - } - else { + } else { throw Exception("Interpreter error: Cannot assign variable $name which has not been defined. If this is supposed to be new, prefix it with 'let' or 'const'") } } - fun let(name: String, value: Value, constant: Boolean) { + fun let(name: String, value: ASTNode, constant: Boolean) { if (values.contains(name)) { if (values[name]!!.constant) { throw Exception("Interpreter error: Cannot reassign constant $name") @@ -44,11 +43,10 @@ class Environment(val parent: Environment?) { values[name] = ValueAndConstStatus(value, constant) } - fun get(name: String) : Value? { + fun get(name: String): ASTNode? { if (values.contains(name)) { - return values[name]?.value - } - else if(parent != null){ + return values[name]!!.value + } else if (parent != null) { return parent.get(name) } return null diff --git a/src/Interpreter.kt b/src/Interpreter.kt index 0fc4109..2160ab8 100644 --- a/src/Interpreter.kt +++ b/src/Interpreter.kt @@ -1,248 +1,44 @@ -fun interpret(expr: ASTNode?, env: Environment) : Value? { - when(expr) { - is ASTNode.BooleanLiteral -> return Value.BoolValue(expr.value) - is ASTNode.NumberLiteral -> return Value.NumValue(expr.value) - - is ASTNode.Let -> { - if (expr.identifier !is ASTNode.Identifier) { - throw Exception("Interpreter error: Expression after let does not resolve to identifier.") - } - val identifier = expr.identifier - - val result = interpret(expr.expr, Environment(env)) - ?: throw Exception("Interpreter error: No expression on right side of let") - env.let(identifier.name, result, identifier.constant) - return result - } - - is ASTNode.Assign -> { - if (expr.identifier !is ASTNode.Identifier) { - throw Exception("Interpreter error: Expression after let does not resolve to identifier.") - } - val identifier = expr.identifier - - val result = interpret(expr.expr, Environment(env)) - ?: throw Exception("Interpreter error: No expression on right side of let") - env.assign(identifier.name, result, identifier.constant) - return result - } - - is ASTNode.BinOp -> { - val left = interpret(expr.left, Environment(env)) - val right = interpret(expr.right, Environment(env)) - when (expr.binOp) { - BinOpType.Add -> { - checkNumberType(left, "+") - checkNumberType(right, "+") - return Value.NumValue( - (left as Value.NumValue).value + - (right as Value.NumValue).value) - } - BinOpType.Subtract -> { - checkNumberType(left, "-") - checkNumberType(right, "-") - return Value.NumValue( - (left as Value.NumValue).value - - (right as Value.NumValue).value) - } - BinOpType.Multiply -> { - checkNumberType(left, "*") - checkNumberType(right, "*") - return Value.NumValue( - (left as Value.NumValue).value * - (right as Value.NumValue).value) - } - BinOpType.Divide -> { - checkNumberType(left, "/") - checkNumberType(right, "/") - val rightValue = (right as Value.NumValue).value - if (rightValue == 0) { - throw Exception("Division by zero") - } - return Value.NumValue( - (left as Value.NumValue).value / rightValue) - } - BinOpType.Modulus -> { - checkNumberType(left, "%") - checkNumberType(right, "%") - val rightValue = (right as Value.NumValue).value - if (rightValue == 0) { - throw Exception("Modulus by zero") - } - return Value.NumValue( - (left as Value.NumValue).value % rightValue) - } - BinOpType.Lt -> { - checkNumberType(left, "<") - checkNumberType(right, "<") - return Value.BoolValue( - (left as Value.NumValue).value < - (right as Value.NumValue).value) - } - BinOpType.Leq -> { - checkNumberType(left, "<=") - checkNumberType(right, "<=") - return Value.BoolValue( - (left as Value.NumValue).value <= - (right as Value.NumValue).value) - } - BinOpType.Gt -> { - checkNumberType(left, ">") - checkNumberType(right, ">") - return Value.BoolValue( - (left as Value.NumValue).value > - (right as Value.NumValue).value) - } - BinOpType.Geq -> { - checkNumberType(left, ">=") - checkNumberType(right, ">=") - return Value.BoolValue( - (left as Value.NumValue).value >= - (right as Value.NumValue).value) - } - BinOpType.Equal -> { - checkNumberOrBooleanType(left, "==") - checkNumberOrBooleanType(right, "==") - return Value.BoolValue(left == right) - } - BinOpType.Neq -> { - checkNumberOrBooleanType(left, "!=") - checkNumberOrBooleanType(right, "!=") - return Value.BoolValue(left != right) - } - BinOpType.And -> { - checkBooleanType(left, "&&") - checkBooleanType(right, "&&") - return Value.BoolValue( - (left as Value.BoolValue).value && - (right as Value.BoolValue).value) - } - BinOpType.Or -> { - checkBooleanType(left, "||") - checkBooleanType(right, "||") - return Value.BoolValue( - (left as Value.BoolValue).value || - (right as Value.BoolValue).value) - } - } - } - - is ASTNode.UnOp -> { - val operand = interpret(expr.expr, Environment(env)) - when (expr.unOp) { - UnOpType.Negate -> { - checkNumberType(operand, "-") - return Value.NumValue(-(operand as Value.NumValue).value) - } - UnOpType.Not -> { - checkBooleanType(operand, "!") - return Value.BoolValue(!(operand as Value.BoolValue).value) - } - } - } - - is ASTNode.Semicolon -> { - interpret(expr.left, env) - return interpret(expr.right, env) - } - - is ASTNode.Block -> return interpret(expr.expr, Environment(env)) - - is ASTNode.BuiltInCall -> { - val args = ArrayList() - for (arg in expr.params) { - args.add(interpret(arg, Environment(env))) - } - - when (expr.builtIn) { - BuiltInFunction.OutNumber -> { - if (args.size != 1) { - throw Exception("Built-in 'outn' takes exactly 1 argument") - } - - when (args[0]) { - is Value.BoolValue -> println((args[0] as Value.BoolValue).value) - is Value.FuncValue -> println("") - is Value.NumValue -> println((args[0] as Value.NumValue).value) - null -> println("null") - } - return Value.BoolValue(true) - } - } - } - is ASTNode.FunctionCall -> { - val function = interpret(expr.function, Environment(env)) - if (function !is Value.FuncValue) { - throw Exception("Trying to call non-function value") - } - val args = ArrayList() - for (arg in expr.params) { - args.add(interpret(arg, Environment(env))) - } - - val funDecl = function.value.func as ASTNode.FunctionDec - - if (args.size != funDecl.params.size) { - throw Exception("Function call error: Expected " + - funDecl.params.size + " arguments but got " + args.size) - } - - val callEnv = Environment(function.value.env) - for ((i, arg) in args.withIndex()) { - val param = funDecl.params[i] as ASTNode.Identifier - if (arg == null) { - throw Exception("Got undefined argument in function call.") - } - callEnv.let(param.name, arg, param.constant) - } - return interpret(funDecl.expr, callEnv) - } - is ASTNode.FunctionDec -> return Value.FuncValue(Closure(expr, Environment(env))) - is ASTNode.Identifier -> { - if (env.get(expr.name) == null) { - throw Exception("Undefined variable $expr") - } - return env.get(expr.name) - } - is ASTNode.IfElse -> { - - for ((i, cond) in expr.conds.withIndex()) { - val condResult = interpret(cond, Environment(env)) - if(condResult is Value.BoolValue && condResult.value) { - return interpret(expr.ifTrues[i], Environment(env)) - } - } - if (expr.ifFalse != null) { - return interpret(expr.ifFalse, Environment(env)) - } - return Value.BoolValue(false) - } - is ASTNode.While -> { - while((interpret(expr.cond, Environment(env)) as Value.BoolValue).value) { - interpret(expr.whileTrue, Environment(env)) - } - return Value.BoolValue(false) - } - else -> { - throw Exception("Interpreter error: Unknown node type") - } - } +import ast.* +import kotlin.system.exitProcess + +fun interpret(expr: ASTNode): ASTNode { + val globalEnvironment = Environment(null) + + globalEnvironment.assign( + "print", + BuiltInFunctionLiteral(listOf(Identifier("value", true))) { arguments -> + println(arguments[0].asString()) + NullLiteral + }, + true + ) + globalEnvironment.assign( + "panic", + BuiltInFunctionLiteral(listOf(Identifier("value", true))) { arguments -> + println("Program panicked while interpreting:") + println(arguments[0].asString()) + exitProcess(1) + }, + true + ) + + return expr.interpret(globalEnvironment) } -fun checkNumberType(value: Value?, op: String ) { - if(value !is Value.NumValue) { +fun checkNumberType(value: ASTNode, op: String) { + if (value !is NumberLiteral) { throw Exception("Operation $op expected a different type") } } -fun checkBooleanType(value: Value?, op: String ) { - if(value !is Value.BoolValue) { +fun checkBooleanType(value: ASTNode, op: String) { + if (value !is BooleanLiteral) { throw Exception("Operation $op expected a different type") } } -fun checkNumberOrBooleanType (value: Value?, op: String ) { - if(value !is Value.BoolValue && value !is Value.NumValue) { +fun checkNumberOrBooleanType(value: ASTNode, op: String) { + if (value !is BooleanLiteral && value !is NumberLiteral) { throw Exception("Operation $op expected a different type") } } \ No newline at end of file diff --git a/src/Lexer.kt b/src/Lexer.kt deleted file mode 100644 index 44cc9f8..0000000 --- a/src/Lexer.kt +++ /dev/null @@ -1,206 +0,0 @@ -enum class TokenType { - Identifier, - Number, - Boolean, - - Let, - Const, - If, - Else, - ElseIf, - While, - Function, - Arrow, - - OutNumber, - - Semicolon, - - LeftCurlyBracket, - RightCurlyBracket, - LeftParenthesis, - RightParenthesis, - - Plus, - Minus, - Multiply, - Divide, - Modulus, - Gt, - Geq, - EqualEqual, - Leq, - Lt, - NotEqual, - AndAnd, - OrOr, - Comma, - - Not, - Equal, -} - -sealed class Token(val type: TokenType) { - class IntToken(val value: Int, type: TokenType) : Token(type) - class StringToken(val value: String, type: TokenType) : Token(type) - class PlainToken(type: TokenType) : Token(type) -} - - -fun isIdentifierChar(c: Char) : Boolean { - return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' -} - -fun isDigit(c: Char) : Boolean { - return c >= '0' && c <= '9' -} - -data class LexIdentifierResult( - val identifier: String, - val end: Int, -) - -data class LexNumberResult( - val number: Int, - val end: Int -) - -fun lexIdentifier(source: String, start: Int) : LexIdentifierResult { - var result = "" - var end: Int = start - - for((i, chr) in source.substring(start).withIndex()) { - end += 1 - if(isIdentifierChar(chr) || (i > 0 && isDigit(chr))) { - result += chr - } - else { - end -= 1 - break - } - } - - return LexIdentifierResult(result, end - 1) -} - -fun lexNumber(source: String, start: Int) : LexNumberResult { - var result = 0 - var end: Int = start - - for(chr in source.substring(start)) { - end += 1 - if(isDigit(chr)) { - result *= 10 - result += (chr - '0') - } - else { - end -= 1 - break - } - } - return LexNumberResult(result, end - 1) -} - -fun tokenize(input: String) : ArrayList { - val tokens: ArrayList = ArrayList() - - var pos = 0 - while(pos < input.length) { - val current = input[pos] - if(isDigit(current)) { - val numberAndEnd: LexNumberResult = lexNumber(input, pos) - val number: Int = numberAndEnd.number - val end: Int = numberAndEnd.end - pos = end - tokens.add(Token.IntToken(number, TokenType.Number)) - } - else if(isIdentifierChar(current)) { - val keywordAndEnd: LexIdentifierResult = lexIdentifier(input, pos) - val keyword: String = keywordAndEnd.identifier - val end = keywordAndEnd.end - pos = end - when(keyword) { - "let" -> tokens.add(Token.PlainToken(TokenType.Let)) - "const" -> tokens.add(Token.PlainToken(TokenType.Const)) - "if" -> tokens.add(Token.PlainToken(TokenType.If)) - "elif" -> tokens.add(Token.PlainToken(TokenType.ElseIf)) - "else" -> tokens.add(Token.PlainToken(TokenType.Else)) - "func" -> tokens.add(Token.PlainToken(TokenType.Function)) - "while" -> tokens.add(Token.PlainToken(TokenType.While)) - "true", "false" -> tokens.add(Token.StringToken(keyword, TokenType.Boolean)) - "outn" -> tokens.add(Token.PlainToken(TokenType.OutNumber)) - else -> tokens.add(Token.StringToken(keyword,TokenType.Identifier)) - } - } - else { - when(current) { - '(' -> tokens.add(Token.PlainToken(TokenType.LeftParenthesis)) - ')' -> tokens.add(Token.PlainToken(TokenType.RightParenthesis)) - '{' -> tokens.add(Token.PlainToken(TokenType.LeftCurlyBracket)) - '}' -> tokens.add(Token.PlainToken(TokenType.RightCurlyBracket)) - '+' -> tokens.add(Token.PlainToken(TokenType.Plus)) - '-' -> tokens.add(Token.PlainToken(TokenType.Minus)) - '/' -> tokens.add(Token.PlainToken(TokenType.Divide)) - '*' -> tokens.add(Token.PlainToken(TokenType.Multiply)) - '%' -> tokens.add(Token.PlainToken(TokenType.Modulus)) - ';' -> tokens.add(Token.PlainToken(TokenType.Semicolon)) - ',' -> tokens.add(Token.PlainToken(TokenType.Comma)) - '>' -> { - if(pos + 1 < input.length && input[pos + 1] == '=') { - tokens.add(Token.PlainToken(TokenType.Geq)) - pos += 1 - } - else { - tokens.add(Token.PlainToken(TokenType.Gt)) - } - } - '<' -> { - if(pos + 1 < input.length && input[pos + 1] == '=') { - tokens.add(Token.PlainToken(TokenType.Leq)) - pos += 1 - } - else { - tokens.add(Token.PlainToken(TokenType.Lt)) - } - } - '=' -> { - if(pos + 1 < input.length && input[pos + 1] == '=') { - tokens.add(Token.PlainToken(TokenType.EqualEqual)) - pos += 1 - } - else if(pos + 1 < input.length && input[pos + 1] == '>') { - tokens.add(Token.PlainToken(TokenType.Arrow)) - pos += 1 - } - else { - tokens.add(Token.PlainToken(TokenType.Equal)) - } - } - '!' -> { - if(pos + 1 < input.length && input[pos + 1] == '=') { - tokens.add(Token.PlainToken(TokenType.NotEqual)) - pos += 1 - } - else { - tokens.add(Token.PlainToken(TokenType.Not)) - } - } - '|' -> { - if(pos + 1 < input.length && input[pos + 1] == '|') { - tokens.add(Token.PlainToken(TokenType.OrOr)) - pos += 1 - } - } - '&' -> { - if(pos + 1 < input.length && input[pos + 1] == '&') { - tokens.add(Token.PlainToken(TokenType.AndAnd)) - pos += 1 - } - } - - } - } - pos += 1 - } - return tokens -} \ No newline at end of file diff --git a/src/Main.kt b/src/Main.kt index df0144e..34c3c37 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -1,9 +1,10 @@ +import lexer.Lexer import java.io.File //TIP To Run code, press or // click the icon in the gutter. fun main(args: Array) { - if(args.size != 1) { + if (args.size != 1) { println("Usage: flug ") return } @@ -11,14 +12,12 @@ fun main(args: Array) { val file = File(args[0]) val code = file.readText() - val tokens = tokenize(code) + val lexer = Lexer(code) + val tokens = lexer.tokenize() val parser = Parser(tokens) val ast = parser.parse() - when (val interpreted = interpret(ast, Environment(null))) { - is Value.BoolValue -> println(interpreted.value) - is Value.FuncValue -> println("") - is Value.NumValue -> println(interpreted.value) - null -> "null" - } + + val interpreted = interpret(ast) + println(interpreted.asString()) } \ No newline at end of file diff --git a/src/Parser.kt b/src/Parser.kt index 9ff7aaa..34daf07 100644 --- a/src/Parser.kt +++ b/src/Parser.kt @@ -1,7 +1,10 @@ +import ast.* +import lexer.Token +import lexer.TokenType class Parser( - private val tokens: ArrayList, -){ + private val tokens: List, +) { private var current: Int = 0 fun parse(): ASTNode { @@ -14,24 +17,24 @@ class Parser( while (match(TokenType.Semicolon)) { val right = parseFunctionDec() - left = ASTNode.Semicolon(left, right) + left = Semicolon(left, right) } return left } private fun parseFunctionDec(): ASTNode { - if(match(TokenType.Function)) { + if (match(TokenType.Function)) { consume(TokenType.LeftParenthesis, "expected '(' after 'function'.") - val params = ArrayList() + val params = mutableListOf() while (true) { if (check(TokenType.RightParenthesis)) break val isConstant = match(TokenType.Const) if (!check(TokenType.Identifier)) { throw Exception("Parser error: expected identifier in parameter list.") } - params += ASTNode.Identifier((advance() as Token.StringToken).value , isConstant) + params += Identifier((advance()).value!!, isConstant) if (!check(TokenType.Comma)) break advance() } @@ -41,40 +44,13 @@ class Parser( val body = parse() consume(TokenType.RightCurlyBracket, "expected '}' to close function body.") - return ASTNode.FunctionDec(params, body) + return FunctionLiteral(params, body, null) } return parseStatement() } private fun parseStatement(): ASTNode { - if(match(TokenType.If)) { - val conds = ArrayList() - val ifTrues = ArrayList() - - do { - consume(TokenType.LeftParenthesis, "expected '(' after 'if'.") - conds += parse() - consume(TokenType.RightParenthesis, "expected ')' after 'if' condition.") - - consume(TokenType.LeftCurlyBracket, "expected '{' to begin 'if' block.") - ifTrues += parse() - consume(TokenType.RightCurlyBracket, "expected '}' to close 'if' block.") - - if(!match(TokenType.ElseIf)) { - break - } - } while(true) - - var ifFalse: ASTNode? = null - if(peek().type == TokenType.Else) { - advance() - consume(TokenType.LeftCurlyBracket, "expected '{' to begin 'else' block.") - ifFalse = parse() - consume(TokenType.RightCurlyBracket, "expected '}' to close 'else' block.") - } - return ASTNode.IfElse(conds, ifTrues, ifFalse) - } - else if (match(TokenType.While)) { + if (match(TokenType.While)) { consume(TokenType.LeftParenthesis, "expected '(' after 'while'.") val cond = parse() consume(TokenType.RightParenthesis, "expected ')' after 'while' condition.") @@ -83,27 +59,17 @@ class Parser( val whileTrue = parse() consume(TokenType.RightCurlyBracket, "expected '}' to close 'while' block.") - return ASTNode.While(cond, whileTrue) - } - else if (match(TokenType.Let)) { - if(!check(TokenType.Identifier)) { + return While(cond, whileTrue) + } else if (match(TokenType.Let) || match(TokenType.Const)) { + val variableType = previous() + if (!check(TokenType.Identifier)) { throw Exception("Parser error: expected identifier after 'let'.") } - val name = (peek() as Token.StringToken).value + val name = peek().value!! advance() consume(TokenType.Equal, "expected '=' after 'let' declaration.") val value = parseFunctionDec() - return ASTNode.Let(ASTNode.Identifier(name, false), value) - } - else if (match(TokenType.Const)) { - if(!check(TokenType.Identifier)) { - throw Exception("Parser error: expected identifier after 'let'.") - } - val name = (peek() as Token.StringToken).value - advance() - consume(TokenType.Equal, "expected '=' after 'const' declaration.") - val value = parseFunctionDec() - return ASTNode.Let(ASTNode.Identifier(name, true), value) + return Let(Identifier(name, variableType.type == TokenType.Const), value) } return parseAssign() } @@ -112,12 +78,11 @@ class Parser( var left = parseOr() - while(true) { + while (true) { if (match(TokenType.Equal)) { val right = parseFunctionDec() - left = ASTNode.Assign(left, right) - } - else { + left = Assign(left, right) + } else { break } } @@ -128,11 +93,11 @@ class Parser( private fun parseOr(): ASTNode { var left = parseAnd() while (true) { - if(match (TokenType.OrOr)) { + if (match(TokenType.OrOr)) { + val orType = previous() val right = parseAnd() - left = ASTNode.BinOp(BinOpType.Or, left, right) - } - else { + left = BinaryExpression(orType.type, left, right) + } else { break } } @@ -142,12 +107,12 @@ class Parser( private fun parseAnd(): ASTNode { var left = parseEquality() while (true) { - if(match (TokenType.AndAnd)) { + if (match(TokenType.AndAnd)) { + val andType = previous() val right = parseEquality() - left = ASTNode.BinOp(BinOpType.And, left, right) - } - else { + left = BinaryExpression(andType.type, left, right) + } else { break } } @@ -158,15 +123,11 @@ class Parser( var left = parseComparison() while (true) { - if(match (TokenType.EqualEqual)) { + if (match(TokenType.EqualEqual) || match(TokenType.NotEqualTo)) { + val equalityType = previous() val right = parseComparison() - left = ASTNode.BinOp(BinOpType.Equal, left, right) - } - else if(match (TokenType.NotEqual)) { - val right = parseComparison() - left = ASTNode.BinOp(BinOpType.Neq, left, right) - } - else { + left = BinaryExpression(equalityType.type, left, right) + } else { break } } @@ -176,23 +137,19 @@ class Parser( private fun parseComparison(): ASTNode { var left = parseTerm() while (true) { - if(match (TokenType.Leq)) { + if (match(TokenType.LessThanOrEqualTo)) { val right = parseTerm() - left = ASTNode.BinOp(BinOpType.Leq, left, right) - } - else if(match (TokenType.Lt)) { + left = BinaryExpression(TokenType.LessThanOrEqualTo, left, right) + } else if (match(TokenType.LessThan)) { val right = parseTerm() - left = ASTNode.BinOp(BinOpType.Lt, left, right) - } - else if(match (TokenType.Gt)) { + left = BinaryExpression(TokenType.LessThan, left, right) + } else if (match(TokenType.GreaterThan)) { val right = parseTerm() - left = ASTNode.BinOp(BinOpType.Gt, left, right) - } - else if(match (TokenType.Geq)) { + left = BinaryExpression(TokenType.GreaterThan, left, right) + } else if (match(TokenType.GreaterThanOrEqualTo)) { val right = parseTerm() - left = ASTNode.BinOp(BinOpType.Geq, left, right) - } - else { + left = BinaryExpression(TokenType.GreaterThanOrEqualTo, left, right) + } else { break } } @@ -202,15 +159,13 @@ class Parser( private fun parseTerm(): ASTNode { var left = parseFactor() while (true) { - if(match (TokenType.Plus)) { + if (match(TokenType.Plus)) { val right = parseFactor() - left = ASTNode.BinOp(BinOpType.Add, left, right) - } - else if (match(TokenType.Minus)) { + left = BinaryExpression(TokenType.Plus, left, right) + } else if (match(TokenType.Minus)) { val right = parseFactor() - left = ASTNode.BinOp(BinOpType.Subtract, left, right) - } - else { + left = BinaryExpression(TokenType.Minus, left, right) + } else { break } } @@ -219,56 +174,36 @@ class Parser( private fun parseFactor(): ASTNode { var left = - parseUnary() ?: throw Exception("Parser error: expected an expression before '*', '/', or '%' operator.") + parseUnary() while (true) { if (match(TokenType.Multiply)) { - val right = parseUnary() ?: throw Exception("Parser error: expected an expression after '*' operator.") - left = ASTNode.BinOp(BinOpType.Multiply, left, right) - } - else if (match(TokenType.Divide)) { - val right = parseUnary() ?: throw Exception("Parser error: expected an expression after '/' operator.") - left = ASTNode.BinOp(BinOpType.Divide, left, right) - } - else if (match(TokenType.Modulus)) { - val right = parseUnary() ?: throw Exception("Parser error: expected an expression after '%' operator.") - left = ASTNode.BinOp(BinOpType.Modulus, left, right) - } - else { + val right = parseUnary() + left = BinaryExpression(TokenType.Multiply, left, right) + } else if (match(TokenType.Divide)) { + val right = parseUnary() + left = BinaryExpression(TokenType.Divide, left, right) + } else if (match(TokenType.Modulus)) { + val right = parseUnary() + left = BinaryExpression(TokenType.Modulus, left, right) + } else { break } } return left } - private fun parseUnary() : ASTNode? { - if (match(TokenType.Minus)) { - val right = - parseUnary() ?: throw Exception("Parser error: expected an expression after unary '-' operator.") - return ASTNode.UnOp(UnOpType.Negate, right) - } - else if (match(TokenType.Not)) { - val right = - parseUnary() ?: throw Exception("Parser error: expected an expression after unary '!' operator.") - return ASTNode.UnOp(UnOpType.Not, right) - } - return parseBuiltInFunction() - } - - private fun parseBuiltInFunction(): ASTNode? { - val params: ArrayList = ArrayList() - if(match(TokenType.OutNumber)) { - consume(TokenType.LeftParenthesis, "Expected '(' after built in function name.") - val param = parseOr() - params += param - consume(TokenType.RightParenthesis, "Expected ')' after function arguments.") - return ASTNode.BuiltInCall(BuiltInFunction.OutNumber, params) + private fun parseUnary(): ASTNode { + if (match(TokenType.Minus) || match(TokenType.Not)) { + val unaryType = previous() + val value = parseUnary() + return UnaryExpression(unaryType.type, value) } return parseFunctionCall() } - private fun parseFunctionCall(): ASTNode? { + private fun parseFunctionCall(): ASTNode { var left = parseParenthesis() - val params: ArrayList = ArrayList() + val params: MutableList = mutableListOf() if (match(TokenType.LeftParenthesis)) { while (true) { if (check(TokenType.RightParenthesis)) { @@ -282,41 +217,67 @@ class Parser( } } consume(TokenType.RightParenthesis, "Expected ')' after function arguments.") - if (left != null) { - left = ASTNode.FunctionCall(left, params) - } + left = FunctionCall(left, params) } return left } - private fun parseParenthesis(): ASTNode? { + private fun parseParenthesis(): ASTNode { if (match(TokenType.LeftParenthesis)) { val expr = parse() consume(TokenType.RightParenthesis, "expected ')' after expression.") return expr } - else if (match(TokenType.LeftCurlyBracket)) { - val expr = parse() - consume(TokenType.RightCurlyBracket, "expected '}' after expression.") - return expr - } + return parseLiteral() } - private fun parseLiteral() : ASTNode? { + private fun parseLiteral(): ASTNode { if (match(TokenType.Identifier)) { - val prev = previous() as Token.StringToken - return ASTNode.Identifier(prev.value, true) - } - else if (match(TokenType.Number)) { - val prev = previous() as Token.IntToken - return ASTNode.NumberLiteral(prev.value) + val prev = previous() + return Identifier(prev.value!!, true) + } else if (match(TokenType.Number)) { + val prev = previous() + return NumberLiteral(prev.value!!.toInt()) + } else if (match(TokenType.Boolean)) { + val prev = previous() + return BooleanLiteral(prev.value!! == "true") + } else if (match(TokenType.Null)) { + val prev = previous() + return BooleanLiteral(prev.value!! == "true") + } else if (match(TokenType.LeftCurlyBracket)) { + val expr = parseSemicolon() + consume(TokenType.RightCurlyBracket, "Expected '}' to close block.") + return expr + } else if (match(TokenType.If)) { + val conds = mutableListOf() + val ifTrues = mutableListOf() + + do { + consume(TokenType.LeftParenthesis, "expected '(' after 'if'.") + conds += parse() + consume(TokenType.RightParenthesis, "expected ')' after 'if' condition.") + + ifTrues += parseLiteral() + + if (!match(TokenType.ElseIf)) { + break + } + } while (true) + + var ifFalse: ASTNode? = null + if (peek().type == TokenType.Else) { + advance() + ifFalse = parseLiteral() + } + return IfElse(conds, ifTrues, ifFalse) } - else if (match(TokenType.Boolean)) { - val prev = previous() as Token.StringToken - return ASTNode.BooleanLiteral(prev.value == "true") + + if (isAtEnd()) { + throw Exception("Expected literal but found EOF") + } else { + throw Exception("Expected literal but found '" + peek().type + "'") } - return null } @@ -330,17 +291,17 @@ class Parser( private fun check(type: TokenType) = !isAtEnd() && type == peek().type - private fun consume(type: TokenType, error: String) : Token{ - if(check(type)) { + private fun consume(type: TokenType, error: String): Token { + if (check(type)) { return advance() } throw Exception("Parer error: $error") } - private fun match(type: TokenType) : Boolean { - if(!isAtEnd()) { + private fun match(type: TokenType): Boolean { + if (!isAtEnd()) { val nextType = peek().type - if(type == nextType) { + if (type == nextType) { advance() return true } diff --git a/src/Value.kt b/src/Value.kt deleted file mode 100644 index 2d865b9..0000000 --- a/src/Value.kt +++ /dev/null @@ -1,7 +0,0 @@ -sealed class Value{ - data class NumValue(val value: Int) : Value() - data class BoolValue(val value: Boolean) : Value() - data class FuncValue(val value: Closure) : Value() -} - -data class Closure(val func: ASTNode?, val env: Environment) \ No newline at end of file diff --git a/src/ast/ASTNode.kt b/src/ast/ASTNode.kt new file mode 100644 index 0000000..b415cef --- /dev/null +++ b/src/ast/ASTNode.kt @@ -0,0 +1,17 @@ +package ast + +import Environment + +sealed class ASTNode { + open fun isCallable(): Boolean = false + + open fun call(arguments: List, env: Environment): ASTNode { + throw Exception("Cannot call non-callable value") + } + + open fun asString(): String { + TODO("String representation not implemented for '" + this.javaClass.simpleName + "'") + } + + abstract fun interpret(env: Environment): ASTNode +} diff --git a/src/ast/Assign.kt b/src/ast/Assign.kt new file mode 100644 index 0000000..1576a96 --- /dev/null +++ b/src/ast/Assign.kt @@ -0,0 +1,19 @@ +package ast + +import Environment + +data class Assign( + val identifier: ASTNode, + val expr: ASTNode +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + if (identifier !is Identifier) { + throw Exception("Interpreter error: Expression after let does not resolve to identifier.") + } + val identifier = identifier + + val result = expr.interpret(Environment(env)) + env.assign(identifier.name, result, identifier.constant) + return result + } +} \ No newline at end of file diff --git a/src/ast/BinaryExpression.kt b/src/ast/BinaryExpression.kt new file mode 100644 index 0000000..0c6a5b0 --- /dev/null +++ b/src/ast/BinaryExpression.kt @@ -0,0 +1,140 @@ +package ast + +import Environment +import checkBooleanType +import checkNumberOrBooleanType +import checkNumberType +import lexer.TokenType + +data class BinaryExpression( + val operator: TokenType, + val left: ASTNode, + val right: ASTNode +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + val left = left.interpret(Environment(env)) + val right = right.interpret(Environment(env)) + when (operator) { + TokenType.Plus -> { + checkNumberType(left, "+") + checkNumberType(right, "+") + return NumberLiteral( + (left as NumberLiteral).value + + (right as NumberLiteral).value + ) + } + + TokenType.Minus -> { + checkNumberType(left, "-") + checkNumberType(right, "-") + return NumberLiteral( + (left as NumberLiteral).value - + (right as NumberLiteral).value + ) + } + + TokenType.Multiply -> { + checkNumberType(left, "*") + checkNumberType(right, "*") + return NumberLiteral( + (left as NumberLiteral).value * + (right as NumberLiteral).value + ) + } + + TokenType.Divide -> { + checkNumberType(left, "/") + checkNumberType(right, "/") + val rightASTNode = (right as NumberLiteral).value + if (rightASTNode == 0) { + throw Exception("Division by zero") + } + return NumberLiteral( + (left as NumberLiteral).value / rightASTNode + ) + } + + TokenType.Modulus -> { + checkNumberType(left, "%") + checkNumberType(right, "%") + val rightASTNode = (right as NumberLiteral).value + if (rightASTNode == 0) { + throw Exception("Modulus by zero") + } + return NumberLiteral( + (left as NumberLiteral).value % rightASTNode + ) + } + + TokenType.LessThan -> { + checkNumberType(left, "<") + checkNumberType(right, "<") + return BooleanLiteral( + (left as NumberLiteral).value < + (right as NumberLiteral).value + ) + } + + TokenType.LessThanOrEqualTo -> { + checkNumberType(left, "<=") + checkNumberType(right, "<=") + return BooleanLiteral( + (left as NumberLiteral).value <= + (right as NumberLiteral).value + ) + } + + TokenType.GreaterThan -> { + checkNumberType(left, ">") + checkNumberType(right, ">") + return BooleanLiteral( + (left as NumberLiteral).value > + (right as NumberLiteral).value + ) + } + + TokenType.GreaterThanOrEqualTo -> { + checkNumberType(left, ">=") + checkNumberType(right, ">=") + return BooleanLiteral( + (left as NumberLiteral).value >= + (right as NumberLiteral).value + ) + } + + TokenType.EqualEqual -> { + checkNumberOrBooleanType(left, "==") + checkNumberOrBooleanType(right, "==") + return BooleanLiteral(left == right) + } + + TokenType.NotEqualTo -> { + checkNumberOrBooleanType(left, "!=") + checkNumberOrBooleanType(right, "!=") + return BooleanLiteral(left != right) + } + + TokenType.AndAnd -> { + checkBooleanType(left, "&&") + checkBooleanType(right, "&&") + return BooleanLiteral( + (left as BooleanLiteral).value && + (right as BooleanLiteral).value + ) + } + + TokenType.OrOr -> { + checkBooleanType(left, "||") + checkBooleanType(right, "||") + return BooleanLiteral( + (left as BooleanLiteral).value || + (right as BooleanLiteral).value + ) + } + + else -> { + throw NotImplementedError("Binary operator '$operator'") + } + } + } +} \ No newline at end of file diff --git a/src/ast/BooleanLiteral.kt b/src/ast/BooleanLiteral.kt new file mode 100644 index 0000000..578f1ea --- /dev/null +++ b/src/ast/BooleanLiteral.kt @@ -0,0 +1,9 @@ +package ast + +import Environment + +data class BooleanLiteral(val value: Boolean) : ASTNode() { + override fun asString() = value.toString() + + override fun interpret(env: Environment): ASTNode = this +} \ No newline at end of file diff --git a/src/ast/BuiltInFunctionLiteral.kt b/src/ast/BuiltInFunctionLiteral.kt new file mode 100644 index 0000000..6477936 --- /dev/null +++ b/src/ast/BuiltInFunctionLiteral.kt @@ -0,0 +1,24 @@ +package ast + +import Environment + +data class BuiltInFunctionLiteral( + val params: List, + val body: (List) -> ASTNode +) : ASTNode() { + override fun call(arguments: List, env: Environment): ASTNode { + if (arguments.size != params.size) { + throw Exception( + "Function call error: Expected " + + params.size + " arguments but got " + arguments.size + ) + } + + return body(arguments) + } + + override fun isCallable(): Boolean = true + + override fun asString(): String = "" + override fun interpret(env: Environment): ASTNode = this +} \ No newline at end of file diff --git a/src/ast/FunctionCall.kt b/src/ast/FunctionCall.kt new file mode 100644 index 0000000..8353651 --- /dev/null +++ b/src/ast/FunctionCall.kt @@ -0,0 +1,21 @@ +package ast + +import Environment + +data class FunctionCall( + val function: ASTNode, + val params: List +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + val function = function.interpret(Environment(env)) + if (!function.isCallable()) { + throw Exception("Cannot call non-callable value") + } + val args = ArrayList() + for (arg in params) { + args.add(arg.interpret(Environment(env))) + } + + return function.call(args, env) + } +} \ No newline at end of file diff --git a/src/ast/FunctionLiteral.kt b/src/ast/FunctionLiteral.kt new file mode 100644 index 0000000..f69f22d --- /dev/null +++ b/src/ast/FunctionLiteral.kt @@ -0,0 +1,31 @@ +package ast + +import Environment + +data class FunctionLiteral( + val params: List, + val body: ASTNode, + val closure: Environment? +) : ASTNode() { + override fun call(arguments: List, env: Environment): ASTNode { + if (arguments.size != params.size) { + throw Exception( + "Function call error: Expected " + + params.size + " arguments but got " + arguments.size + ) + } + + val callEnv = Environment(closure ?: env) + for ((i, arg) in arguments.withIndex()) { + val param = params[i] as Identifier + callEnv.let(param.name, arg, param.constant) + } + + return body.interpret(callEnv) + } + + override fun isCallable(): Boolean = true + + override fun asString(): String = "" + override fun interpret(env: Environment): ASTNode = this +} \ No newline at end of file diff --git a/src/ast/Identifier.kt b/src/ast/Identifier.kt new file mode 100644 index 0000000..1a5c3bb --- /dev/null +++ b/src/ast/Identifier.kt @@ -0,0 +1,10 @@ +package ast + +import Environment + +data class Identifier(val name: String, val constant: Boolean) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + return env.get(name) + ?: throw Exception("Undefined variable $name") + } +} \ No newline at end of file diff --git a/src/ast/IfElse.kt b/src/ast/IfElse.kt new file mode 100644 index 0000000..31da69a --- /dev/null +++ b/src/ast/IfElse.kt @@ -0,0 +1,22 @@ +package ast + +import Environment + +data class IfElse( + val conds: List, + val ifTrues: List, + val ifFalse: ASTNode? +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + for ((i, cond) in conds.withIndex()) { + val condResult = cond.interpret(Environment(env)) + if (condResult is BooleanLiteral && condResult.value) { + return ifTrues[i].interpret(Environment(env)) + } + } + if (ifFalse != null) { + return ifFalse.interpret(Environment(env)) + } + return BooleanLiteral(false) + } +} \ No newline at end of file diff --git a/src/ast/Let.kt b/src/ast/Let.kt new file mode 100644 index 0000000..a883957 --- /dev/null +++ b/src/ast/Let.kt @@ -0,0 +1,19 @@ +package ast + +import Environment + +data class Let( + val identifier: ASTNode, + val expr: ASTNode +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + if (identifier !is Identifier) { + throw Exception("Interpreter error: Expression after let does not resolve to identifier.") + } + val identifier = identifier + + val result = expr.interpret(Environment(env)) + env.let(identifier.name, result, identifier.constant) + return result + } +} \ No newline at end of file diff --git a/src/ast/NullLiteral.kt b/src/ast/NullLiteral.kt new file mode 100644 index 0000000..1b7c8ef --- /dev/null +++ b/src/ast/NullLiteral.kt @@ -0,0 +1,8 @@ +package ast + +import Environment + +object NullLiteral : ASTNode() { + override fun asString(): String = "null" + override fun interpret(env: Environment): ASTNode = this +} \ No newline at end of file diff --git a/src/ast/NumberLiteral.kt b/src/ast/NumberLiteral.kt new file mode 100644 index 0000000..240d9a9 --- /dev/null +++ b/src/ast/NumberLiteral.kt @@ -0,0 +1,8 @@ +package ast + +import Environment + +data class NumberLiteral(val value: Int) : ASTNode() { + override fun asString() = value.toString() + override fun interpret(env: Environment): ASTNode = this +} \ No newline at end of file diff --git a/src/ast/Semicolon.kt b/src/ast/Semicolon.kt new file mode 100644 index 0000000..6155ef3 --- /dev/null +++ b/src/ast/Semicolon.kt @@ -0,0 +1,13 @@ +package ast + +import Environment + +data class Semicolon( + val left: ASTNode, + val right: ASTNode +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + left.interpret(env) + return right.interpret(env) + } +} \ No newline at end of file diff --git a/src/ast/UnaryExpression.kt b/src/ast/UnaryExpression.kt new file mode 100644 index 0000000..841bc19 --- /dev/null +++ b/src/ast/UnaryExpression.kt @@ -0,0 +1,28 @@ +package ast + +import Environment +import checkBooleanType +import checkNumberType +import lexer.TokenType + +data class UnaryExpression( + val operator: TokenType, + val expr: ASTNode +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + val operand = expr.interpret(Environment(env)) + when (operator) { + TokenType.Minus -> { + checkNumberType(operand, "-") + return NumberLiteral(-(operand as NumberLiteral).value) + } + + TokenType.Not -> { + checkBooleanType(operand, "!") + return BooleanLiteral(!(operand as BooleanLiteral).value) + } + + else -> throw NotImplementedError("Unary operator '" + operator + "'") + } + } +} \ No newline at end of file diff --git a/src/ast/While.kt b/src/ast/While.kt new file mode 100644 index 0000000..6768123 --- /dev/null +++ b/src/ast/While.kt @@ -0,0 +1,16 @@ +package ast + +import Environment + +data class While( + val cond: ASTNode, + val whileTrue: ASTNode +) : ASTNode() { + override fun interpret(env: Environment): ASTNode { + while ((cond.interpret(Environment(env)) as BooleanLiteral).value) { + whileTrue.interpret(Environment(env)) + } + + return BooleanLiteral(false) + } +} \ No newline at end of file diff --git a/src/lexer/Lexer.kt b/src/lexer/Lexer.kt new file mode 100644 index 0000000..7a3f515 --- /dev/null +++ b/src/lexer/Lexer.kt @@ -0,0 +1,147 @@ +package lexer + +fun Char.isIdentifierChar(): Boolean { + return (this >= 'a' && this <= 'z') || (this >= 'A' && this <= 'Z') || this == '_' +} + +class Lexer(val input: String) { + private var pos: Int = 0 + private val tokens: MutableList = mutableListOf() + + private fun match(char: Char): Boolean { + if (pos + 1 < input.length && input[pos] == char) { + pos++ + + return true + } + + return false + } + + fun lexIdentifier(source: String): String { + var result = "" + + for ((i, char) in source.substring(pos).withIndex()) { + if (char.isIdentifierChar() || (i > 0 && char.isDigit())) { + pos++ + result += char + } else { + break + } + } + + return result + } + + fun lexNumber(source: String): String { + var result = "" + + for (char in source.substring(pos)) { + if (char.isDigit()) { + result += char + pos++ + } else { + break + } + } + return result + } + + fun addToken(type: TokenType, value: String) { + tokens.add(Token(type, value)) + } + + fun addToken(type: TokenType) { + tokens.add(Token(type)) + } + + fun tokenize(): List { + while (pos < input.length) { + val current = input[pos] + if (current.isDigit()) { + val number = lexNumber(input) + addToken(TokenType.Number, number) + } else if (current.isIdentifierChar()) { + val identifier = lexIdentifier(input) + when (identifier) { + "let" -> addToken(TokenType.Let) + "const" -> addToken(TokenType.Const) + "if" -> addToken(TokenType.If) + "elif" -> addToken(TokenType.ElseIf) + "else" -> addToken(TokenType.Else) + "func" -> addToken(TokenType.Function) + "while" -> addToken(TokenType.While) + "true", "false" -> addToken(TokenType.Boolean, identifier) + "null" -> addToken(TokenType.Null) + else -> addToken(TokenType.Identifier, identifier) + } + } else if (current.isWhitespace()) { + pos++ + } else { + pos++ + when (current) { + '(' -> addToken(TokenType.LeftParenthesis) + ')' -> addToken(TokenType.RightParenthesis) + '{' -> addToken(TokenType.LeftCurlyBracket) + '}' -> addToken(TokenType.RightCurlyBracket) + '+' -> addToken(TokenType.Plus) + '-' -> addToken(TokenType.Minus) + '/' -> addToken(TokenType.Divide) + '*' -> addToken(TokenType.Multiply) + '%' -> addToken(TokenType.Modulus) + ';' -> addToken(TokenType.Semicolon) + ',' -> addToken(TokenType.Comma) + '>' -> { + if (match('=')) { + addToken(TokenType.GreaterThanOrEqualTo) + } else { + addToken(TokenType.GreaterThan) + } + } + + '<' -> { + if (match('=')) { + addToken(TokenType.LessThanOrEqualTo) + } else { + addToken(TokenType.LessThan) + } + } + + '=' -> { + if (match('=')) { + addToken(TokenType.EqualEqual) + } else if (match('>')) { + addToken(TokenType.Arrow) + } else { + addToken(TokenType.Equal) + } + } + + '!' -> { + if (match('=')) { + addToken(TokenType.NotEqualTo) + } else { + addToken(TokenType.Not) + } + } + + '|' -> { + if (match('|')) { + addToken(TokenType.OrOr) + } + } + + '&' -> { + if (match('&')) { + addToken(TokenType.AndAnd) + } + } + + else -> throw Exception("Unexpected character '$current'") + } + } + } + + return tokens + } +} \ No newline at end of file diff --git a/src/lexer/Token.kt b/src/lexer/Token.kt new file mode 100644 index 0000000..aa6cc5a --- /dev/null +++ b/src/lexer/Token.kt @@ -0,0 +1,5 @@ +package lexer + +data class Token(val type: TokenType, val value: String?) { + constructor(type: TokenType) : this(type, null) +} \ No newline at end of file diff --git a/src/lexer/TokenType.kt b/src/lexer/TokenType.kt new file mode 100644 index 0000000..23ec236 --- /dev/null +++ b/src/lexer/TokenType.kt @@ -0,0 +1,42 @@ +package lexer + +enum class TokenType { + Identifier, + Number, + Boolean, + Null, + + Let, + Const, + If, + Else, + ElseIf, + While, + Function, + Arrow, + + Semicolon, + + LeftCurlyBracket, + RightCurlyBracket, + LeftParenthesis, + RightParenthesis, + + Plus, + Minus, + Multiply, + Divide, + Modulus, + GreaterThan, + GreaterThanOrEqualTo, + EqualEqual, + LessThanOrEqualTo, + LessThan, + NotEqualTo, + AndAnd, + OrOr, + Comma, + + Not, + Equal, +} \ No newline at end of file diff --git a/test/test_closure.flug b/test/test_closure.flug index ea36763..f1cc3c4 100644 --- a/test/test_closure.flug +++ b/test/test_closure.flug @@ -19,7 +19,7 @@ let count_constructor = func(init) => { }; let my_count = count_constructor(3); -outn(my_count(1)); -outn(my_count(1)); -outn(my_count(1)); -outn(my_count(0)) \ No newline at end of file +print(my_count(1)); +print(my_count(1)); +print(my_count(1)); +print(my_count(0)) \ No newline at end of file diff --git a/test/test_panic.flug b/test/test_panic.flug new file mode 100644 index 0000000..280fcb5 --- /dev/null +++ b/test/test_panic.flug @@ -0,0 +1 @@ +panic(false) diff --git a/test/test_print.flug b/test/test_print.flug new file mode 100644 index 0000000..4352808 --- /dev/null +++ b/test/test_print.flug @@ -0,0 +1,4 @@ +print(1); +print(2 + 3); +print(true); +print(false) diff --git a/test/test_twocounters.flug b/test/test_twocounters.flug index 59a065b..e9ed0ac 100644 --- a/test/test_twocounters.flug +++ b/test/test_twocounters.flug @@ -20,10 +20,10 @@ const count_constructor = func(const init) => { const a = count_constructor(3); const b = count_constructor(0); -outn(a(0)); -outn(b(0)); +print(a(0)); +print(b(0)); b(1); a(2); -outn(a(0)); -outn(b(0)) +print(a(0)); +print(b(0)) diff --git a/test/test_while.flug b/test/test_while.flug index f9b5ef9..56b56cd 100644 --- a/test/test_while.flug +++ b/test/test_while.flug @@ -9,7 +9,7 @@ const fib = func(const x) => { let x = 0; while(x <= 10) { - outn(fib(x)); + print(fib(x)); x = x + 1 }; -0 \ No newline at end of file +null \ No newline at end of file