diff --git a/src/chalk.cr b/src/chalk.cr index 3c58ef2..6b498ad 100644 --- a/src/chalk.cr +++ b/src/chalk.cr @@ -1,6 +1,171 @@ require "./chalk/*" -# TODO: Write documentation for `Chalk` module Chalk - # TODO: Put your code here + lexer = Lexer.new + tokens = lexer.lex(File.read("test.txt")) + + def self.create_op(atom, op) + pl = PlaceholderParser(Tree).new + recurse = atom.then(op).then(pl).transform do |arr| + arr = arr.flatten + TreeOp.new( + arr[1].as(Token).type, + arr[0].as(Tree), + arr[2].as(Tree)).as(Tree) + end + pl.parser = either(recurse, atom) + return pl + end + + def self.create_ops(source, ops) + ops.reduce(source) do |previous, current| + create_op(previous, current) + end + end + + def self.create_type + either(type(TokenType::KwU0), type(TokenType::KwU8), type(TokenType::KwU12)) + end + + def self.create_lit + dec_parser = type(TokenType::LitDec).transform &.string.to_i64 + hex_parser = type(TokenType::LitHex).transform &.string.lchop("0x").to_i64(16) + bin_parser = type(TokenType::LitBin).transform &.string.lchop("0b").to_i64(2) + lit_parser = either(dec_parser, hex_parser, bin_parser).transform { |it| TreeLit.new(it).as(Tree) } + return lit_parser + end + + def self.create_call(expr) + call = type(TokenType::Id).then(char '(').then(delimited(expr, char ',')).then(char ')').transform do |arr| + arr = arr.flatten + name = arr[0].as(Token).string + params = arr[2..arr.size - 2].map &.as(Tree) + TreeCall.new(name, params).as(Tree) + end + return call + end + + def self.create_block(statement) + block = char('{').then(many(statement)).then(char '}').transform do |arr| + arr = arr.flatten + params = arr[1..arr.size - 2].map &.as(Tree) + TreeBlock.new(params).as(Tree) + end + return block + end + + def self.create_if(expr, block) + iff = type(TokenType::KwIf).then(char '(').then(expr).then(char ')').then(block) + .then(optional(type(TokenType::KwElse).then(block))) + .transform do |arr| + arr = arr.flatten + cond = arr[2].as(Tree) + code = arr[4].as(Tree) + otherwise = arr.size == 7 ? arr[6].as(Tree) : nil + TreeIf.new(cond, code, otherwise).as(Tree) + end + return iff + end + + def self.create_while(expr, block) + whilee = type(TokenType::KwWhile).then(char '(').then(expr).then(char ')').then(block).transform do |arr| + arr = arr.flatten + cond = arr[2].as(Tree) + code = arr[4].as(Tree) + TreeWhile.new(cond, code).as(Tree) + end + return whilee + end + + def self.create_return(expr) + returnn = type(TokenType::KwReturn).then(expr).then(char ';').transform do |arr| + arr = arr.flatten + value = arr[1].as(Tree) + TreeReturn.new(value).as(Tree) + end + return returnn + end + + def self.create_func(block, type) + func = type(TokenType::KwFun).then(type(TokenType::Id)) + .then(char '(').then(delimited(type(TokenType::Id), char ',')).then(char ')') + .then(char ':').then(type) + .then(block).transform do |arr| + arr = arr.flatten + name = arr[1].as(Token).string + params = arr[3..arr.size - 5].map &.as(Token).string + code = arr[arr.size - 1].as(Tree) + type = arr[arr.size - 2].as(Token).type + TreeFunction.new(name, params, code).as(Tree) + end + return func + end + + def self.create_var(expr) + var = type(TokenType::KwVar).then(type(TokenType::Id)).then(char '=').then(expr).then(char ';').transform do |arr| + arr = arr.flatten + name = arr[1].as(Token).string + exp = arr[arr.size - 2].as(Tree) + TreeVar.new(name, exp).as(Tree) + end + return var + end + + def self.create_assign(expr) + assign = type(TokenType::Id).then(char '=').then(expr).then(char ';').transform do |arr| + arr = arr.flatten + name = arr[0].as(Token).string + exp = arr[arr.size - 2].as(Tree) + TreeAssign.new(name, exp).as(Tree) + end + return assign + end + + def self.create_basic(expr) + basic = expr.then(char ';').transform do |arr| + arr.flatten[0].as(Tree) + end + return basic + end + + def self.create_expression + expr_place = PlaceholderParser(Tree).new + literal = create_lit + id = type(TokenType::Id).transform { |it| TreeId.new(it.string).as(Tree) } + call = create_call(expr_place) + atom = either(literal, call, id) + + ops = [ either(type(TokenType::OpMul), type(TokenType::OpDiv)), + either(type(TokenType::OpAdd), type(TokenType::OpSub)), + type(TokenType::OpXor), + type(TokenType::OpAnd), + type(TokenType::OpOr) ] + expr = create_ops(atom, ops) + expr_place.parser = expr + + return expr + end + + def self.create_block + expr = create_expression + + statement_place = PlaceholderParser(Tree).new + block = create_block(statement_place) + iff = create_if(expr, block) + whilee = create_while(expr, block) + returnn = create_return(expr) + var = create_var(expr) + assign = create_assign(expr) + basic = create_basic(expr) + statement = either(basic, var, assign, block, iff, whilee, returnn) + statement_place.parser = statement + + return block + end + + func = create_func(create_block, create_type) + body = many(func) + + final = body + final.parse(tokens, 0)[0].each &.accept(PrintVisitor.new) end diff --git a/src/chalk/builder.cr b/src/chalk/builder.cr index 4f645e5..8bcae2b 100644 --- a/src/chalk/builder.cr +++ b/src/chalk/builder.cr @@ -6,6 +6,10 @@ module Chalk return TypeParser.new(type).as(Parser(Token)) end + def self.char(type): Parser(Token) + return CharParser.new(type).as(Parser(Token)) + end + def self.transform(parser : Parser(T), &transform : T -> R) forall T, R return TransformParser.new(parser, &transform).as(Parser(R)) end @@ -22,6 +26,10 @@ module Chalk return ManyParser.new(parser).as(Parser(Array(T))) end + def self.delimited(parser : Parser(T), delimiter : Parser(R)): Parser(Array(T)) forall T, R + return DelimitedParser.new(parser, delimiter).as(Parser(Array(T))) + end + def self.then(first : Parser(T), second : Parser(R)) forall T, R return NextParser.new(first, second).as(Parser(Array(T | R))) end diff --git a/src/chalk/parser.cr b/src/chalk/parser.cr index 3e9797f..0418817 100644 --- a/src/chalk/parser.cr +++ b/src/chalk/parser.cr @@ -32,6 +32,18 @@ module Chalk end end + class CharParser < Parser(Token) + def initialize(@char : Char) + end + + def parse?(tokens, index) + return nil unless index < tokens.size + return nil unless (tokens[index].type == TokenType::Any) && + tokens[index].string[0] == @char + return { tokens[index], index + 1} + end + end + class TransformParser(T, R) < Parser(R) def initialize(@parser : Parser(T), &@block : T -> R) end @@ -84,6 +96,28 @@ module Chalk end end + class DelimitedParser(T, R) < Parser(Array(T)) + def initialize(@parser : Parser(T), @delimiter : Parser(R)) + end + + def parse?(tokens, index) + array = [] of T + first = @parser.parse?(tokens, index) + return { array, index } unless first + first_value, index = first + array << first_value + while delimiter = @delimiter.parse?(tokens, index) + _, new_index = delimiter + new = @parser.parse?(tokens, new_index) + break unless new + new_value, index = new + array << new_value + end + + return { array, index } + end + end + class NextParser(T, R) < Parser(Array(T | R)) def initialize(@first : Parser(T), @second : Parser(R)) end @@ -111,7 +145,7 @@ module Chalk end def parse?(tokens, index) - @parser.try &.parse(tokens, index) + @parser.try &.parse?(tokens, index) end end end diff --git a/src/chalk/print_visitor.cr b/src/chalk/print_visitor.cr index 91fec2b..bb82050 100644 --- a/src/chalk/print_visitor.cr +++ b/src/chalk/print_visitor.cr @@ -59,8 +59,10 @@ module Chalk end end + forward("[call]", TreeCall) forward("[block]", TreeBlock) forward("[var]", TreeVar) + forward("[assign]", TreeAssign) forward("[if]", TreeIf) forward("[while]", TreeWhile) forward("[return]", TreeReturn) diff --git a/src/chalk/tree.cr b/src/chalk/tree.cr index 919c76f..6ce35d8 100644 --- a/src/chalk/tree.cr +++ b/src/chalk/tree.cr @@ -24,6 +24,18 @@ module Chalk def initialize(@lit : Int64) end end + class TreeCall < Tree + property name : String + property params : Array(Tree) + + def initialize(@name : String, @params : Array(Tree)) end + def accept(v : Visitor) + v.visit(self) + @params.each &.accept(v) + v.finish(self) + end + end + class TreeOp < Tree property op : TokenType property left : Tree @@ -73,17 +85,29 @@ module Chalk end end + class TreeAssign < Tree + property name : String + property expr : Tree + + def initialize(@name : String, @expr : Tree) end + def accept(v : Visitor) + v.visit(self) + @expr.accept(v) + v.finish(self) + end + end + class TreeIf < Tree property condition : Tree property block : Tree - property otherwise : Tree + property otherwise : Tree? - def initialize(@condition : Tree, @block : Tree, @otherwise : Tree) end + def initialize(@condition : Tree, @block : Tree, @otherwise : Tree? = nil) end def accept(v : Visitor) v.visit(self) @condition.accept(v) @block.accept(v) - @otherwise.accept(v) + @otherwise.try &.accept(v) v.finish(self) end end