diff --git a/src/chalk.cr b/src/chalk.cr index 4fd5248..548ef4e 100644 --- a/src/chalk.cr +++ b/src/chalk.cr @@ -1,12 +1,12 @@ require "./chalk/*" module Chalk - lexer = Lexer.new - parser = Parser.new + lexer = Lexer.new + parser = Parser.new - tokens = lexer.lex(File.read("test.txt")) - trees = parser.parse?(tokens) - trees.try do |trees| - trees.each &.accept(PrintVisitor.new) - end + tokens = lexer.lex(File.read("test.txt")) + trees = parser.parse?(tokens) + trees.try do |trees| + trees.each &.accept(PrintVisitor.new) + end end diff --git a/src/chalk/builder.cr b/src/chalk/builder.cr index 4cdd103..43dec6c 100644 --- a/src/chalk/builder.cr +++ b/src/chalk/builder.cr @@ -2,37 +2,37 @@ require "./lexer.cr" require "./parsers.cr" module Chalk - module Builder - def type(type): BasicParser(Token) - return TypeParser.new(type).as(BasicParser(Token)) - end - - def char(type): BasicParser(Token) - return CharParser.new(type).as(BasicParser(Token)) - end - - def transform(parser : BasicParser(T), &transform : T -> R) forall T, R - return TransformParser.new(parser, &transform).as(BasicParser(R)) - end - - def optional(parser : BasicParser(T)): BasicParser(T?) forall T - return OptionalParser.new(parser).as(BasicParser(T?)) - end - - def either(*args : BasicParser(T)): BasicParser(T) forall T - return EitherParser.new(args.to_a).as(BasicParser(T)) - end - - def many(parser : BasicParser(T)): BasicParser(Array(T)) forall T - return ManyParser.new(parser).as(BasicParser(Array(T))) - end - - def delimited(parser : BasicParser(T), delimiter : BasicParser(R)): BasicParser(Array(T)) forall T, R - return DelimitedParser.new(parser, delimiter).as(BasicParser(Array(T))) - end - - def then(first : BasicParser(T), second : BasicParser(R)) forall T, R - return NextParser.new(first, second).as(BasicParser(Array(T | R))) - end + module Builder + def type(type) : BasicParser(Token) + return TypeParser.new(type).as(BasicParser(Token)) end + + def char(type) : BasicParser(Token) + return CharParser.new(type).as(BasicParser(Token)) + end + + def transform(parser : BasicParser(T), &transform : T -> R) forall T, R + return TransformParser.new(parser, &transform).as(BasicParser(R)) + end + + def optional(parser : BasicParser(T)) : BasicParser(T?) forall T + return OptionalParser.new(parser).as(BasicParser(T?)) + end + + def either(*args : BasicParser(T)) : BasicParser(T) forall T + return EitherParser.new(args.to_a).as(BasicParser(T)) + end + + def many(parser : BasicParser(T)) : BasicParser(Array(T)) forall T + return ManyParser.new(parser).as(BasicParser(Array(T))) + end + + def delimited(parser : BasicParser(T), delimiter : BasicParser(R)) : BasicParser(Array(T)) forall T, R + return DelimitedParser.new(parser, delimiter).as(BasicParser(Array(T))) + end + + def then(first : BasicParser(T), second : BasicParser(R)) forall T, R + return NextParser.new(first, second).as(BasicParser(Array(T | R))) + end + end end diff --git a/src/chalk/lexer.cr b/src/chalk/lexer.cr index 9923f4f..0d3124c 100644 --- a/src/chalk/lexer.cr +++ b/src/chalk/lexer.cr @@ -1,82 +1,82 @@ require "lex" module Chalk - enum TokenType - Any, - Str, - Id, - LitDec, - LitBin, - LitHex, - OpAdd - OpSub - OpMul - OpDiv - OpOr - OpAnd - OpXor - KwSprite - KwInline - KwFun - KwU0 - KwU8 - KwU12 - KwVar - KwIf - KwElse - KwWhile - KwReturn + enum TokenType + Any, + Str, + Id, + LitDec, + LitBin, + LitHex, + OpAdd + OpSub + OpMul + OpDiv + OpOr + OpAnd + OpXor + KwSprite + KwInline + KwFun + KwU0 + KwU8 + KwU12 + KwVar + KwIf + KwElse + KwWhile + KwReturn + end + + class Token + def initialize(@string : String, @type : TokenType) end - class Token - def initialize(@string : String, @type : TokenType) - end + getter string : String + getter type : TokenType + end - getter string : String - getter type : TokenType + class Lexer + def initialize + @lexer = Lex::Lexer.new + @lexer.add_pattern(".", TokenType::Any.value) + @lexer.add_pattern("\"(\\\\\"|[^\"])*\"", + TokenType::Str.value) + @lexer.add_pattern("[a-zA-Z_][a-zA-Z_0-9]*", + TokenType::Id.value) + @lexer.add_pattern("[0-9]+", + TokenType::LitDec.value) + @lexer.add_pattern("0b[0-1]+", + TokenType::LitBin.value) + @lexer.add_pattern("0x[0-9a-fA-F]+", + TokenType::LitHex.value) + @lexer.add_pattern("\\+", TokenType::OpAdd.value) + @lexer.add_pattern("-", TokenType::OpSub.value) + @lexer.add_pattern("\\*", TokenType::OpMul.value) + @lexer.add_pattern("/", TokenType::OpDiv.value) + @lexer.add_pattern("&", TokenType::OpAdd.value) + @lexer.add_pattern("\\|", TokenType::OpOr.value) + @lexer.add_pattern("^", TokenType::OpXor.value) + @lexer.add_pattern("sprite", TokenType::KwSprite.value) + @lexer.add_pattern("inline", TokenType::KwInline.value) + @lexer.add_pattern("fun", TokenType::KwFun.value) + @lexer.add_pattern("u0", TokenType::KwU0.value) + @lexer.add_pattern("u8", TokenType::KwU8.value) + @lexer.add_pattern("u12", TokenType::KwU12.value) + @lexer.add_pattern("var", TokenType::KwVar.value) + @lexer.add_pattern("if", TokenType::KwIf.value) + @lexer.add_pattern("else", TokenType::KwElse.value) + @lexer.add_pattern("while", TokenType::KwWhile.value) + @lexer.add_pattern("return", TokenType::KwReturn.value) end - class Lexer - def initialize - @lexer = Lex::Lexer.new - @lexer.add_pattern(".", TokenType::Any.value) - @lexer.add_pattern("\"(\\\\\"|[^\"])*\"", - TokenType::Str.value) - @lexer.add_pattern("[a-zA-Z_][a-zA-Z_0-9]*", - TokenType::Id.value) - @lexer.add_pattern("[0-9]+", - TokenType::LitDec.value) - @lexer.add_pattern("0b[0-1]+", - TokenType::LitBin.value) - @lexer.add_pattern("0x[0-9a-fA-F]+", - TokenType::LitHex.value) - @lexer.add_pattern("\\+", TokenType::OpAdd.value) - @lexer.add_pattern("-", TokenType::OpSub.value) - @lexer.add_pattern("\\*", TokenType::OpMul.value) - @lexer.add_pattern("/", TokenType::OpDiv.value) - @lexer.add_pattern("&", TokenType::OpAdd.value) - @lexer.add_pattern("\\|", TokenType::OpOr.value) - @lexer.add_pattern("^", TokenType::OpXor.value) - @lexer.add_pattern("sprite", TokenType::KwSprite.value) - @lexer.add_pattern("inline", TokenType::KwInline.value) - @lexer.add_pattern("fun", TokenType::KwFun.value) - @lexer.add_pattern("u0", TokenType::KwU0.value) - @lexer.add_pattern("u8", TokenType::KwU8.value) - @lexer.add_pattern("u12", TokenType::KwU12.value) - @lexer.add_pattern("var", TokenType::KwVar.value) - @lexer.add_pattern("if", TokenType::KwIf.value) - @lexer.add_pattern("else", TokenType::KwElse.value) - @lexer.add_pattern("while", TokenType::KwWhile.value) - @lexer.add_pattern("return", TokenType::KwReturn.value) - end - - def lex(string) - return @lexer.lex(string) - .select { |t| !t[0][0].whitespace? } - .map do |tuple| - string, id = tuple - Token.new(string, TokenType.new(id)) - end + def lex(string) + return @lexer.lex(string) + .select { |t| !t[0][0].whitespace? } + .map do |tuple| + string, id = tuple + Token.new(string, TokenType.new(id)) end end + end end diff --git a/src/chalk/parser.cr b/src/chalk/parser.cr index 7fb5e2b..5075788 100644 --- a/src/chalk/parser.cr +++ b/src/chalk/parser.cr @@ -1,173 +1,173 @@ require "./builder.cr" module Chalk - class Parser - include Builder + class Parser + include Builder - private def create_type - either(type(TokenType::KwU0), type(TokenType::KwU8), type(TokenType::KwU12)) - end - - private def 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 - - private def create_op_expr(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 - - private def create_op_exprs(atom, ops) - ops.reduce(atom) do |previous, current| - create_op_expr(previous, current) - end - end - - private def 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 - - private def create_expr - 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_op_exprs(atom, ops) - expr_place.parser = expr - - return expr - end - - private def 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 - - private def 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 - - private def create_basic(expr) - basic = expr.then(char ';').transform do |arr| - arr.flatten[0].as(Tree) - end - return basic - end - - private def 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 - - private def 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 - - private def 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 - - private def 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 - - private def create_statement_block - statement_place = PlaceholderParser(Tree).new - expr = create_expr - 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 {statement, block} - end - - private def 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 initialize - _, block = create_statement_block - @parser = many(create_func(block, create_type)).as(BasicParser(Array(Tree))) - end - - def parse?(tokens) - return @parser.parse?(tokens, 0).try &.[0] - end + private def create_type + either(type(TokenType::KwU0), type(TokenType::KwU8), type(TokenType::KwU12)) end + + private def 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 + + private def create_op_expr(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 + + private def create_op_exprs(atom, ops) + ops.reduce(atom) do |previous, current| + create_op_expr(previous, current) + end + end + + private def 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 + + private def create_expr + 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_op_exprs(atom, ops) + expr_place.parser = expr + + return expr + end + + private def 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 + + private def 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 + + private def create_basic(expr) + basic = expr.then(char ';').transform do |arr| + arr.flatten[0].as(Tree) + end + return basic + end + + private def 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 + + private def 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 + + private def 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 + + private def 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 + + private def create_statement_block + statement_place = PlaceholderParser(Tree).new + expr = create_expr + 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 {statement, block} + end + + private def 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 initialize + _, block = create_statement_block + @parser = many(create_func(block, create_type)).as(BasicParser(Array(Tree))) + end + + def parse?(tokens) + return @parser.parse?(tokens, 0).try &.[0] + end + end end diff --git a/src/chalk/parsers.cr b/src/chalk/parsers.cr index 2fd029e..8d530a7 100644 --- a/src/chalk/parsers.cr +++ b/src/chalk/parsers.cr @@ -1,151 +1,149 @@ require "./builder.cr" module Chalk + abstract class BasicParser(T) + abstract def parse?(tokens : Array(Token), + index : Int64) : Tuple(T, Int64)? - abstract class BasicParser(T) - abstract def parse?(tokens : Array(Token), - index : Int64): Tuple(T, Int64)? - def parse(tokens : Array(Token), - index : Int64): Tuple(T, Int64) - return parse?(tokens, index).not_nil! - end - - def transform(&transform : T -> R) forall R - return TransformParser.new(self, &transform).as(BasicParser(R)) - end - - def then(other : BasicParser(R)): BasicParser(Array(T | R)) forall R - return NextParser - .new(self, other) - .as(BasicParser(Array(T | R))) - end + def parse(tokens : Array(Token), + index : Int64) : Tuple(T, Int64) + return parse?(tokens, index).not_nil! end - class TypeParser < BasicParser(Token) - def initialize(@type : TokenType) - end - - def parse?(tokens, index) - return nil unless index < tokens.size - return nil unless tokens[index].type == @type - return { tokens[index], index + 1} - end + def transform(&transform : T -> R) forall R + return TransformParser.new(self, &transform).as(BasicParser(R)) end - class CharParser < BasicParser(Token) - def initialize(@char : Char) - end + def then(other : BasicParser(R)) : BasicParser(Array(T | R)) forall R + return NextParser.new(self, other).as(BasicParser(Array(T | R))) + end + 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 + class TypeParser < BasicParser(Token) + def initialize(@type : TokenType) end - class TransformParser(T, R) < BasicParser(R) - def initialize(@parser : BasicParser(T), &@block : T -> R) - end + def parse?(tokens, index) + return nil unless index < tokens.size + return nil unless tokens[index].type == @type + return {tokens[index], index + 1} + end + end - def parse?(tokens, index) - if parsed = @parser.parse?(tokens, index) - return { @block.call(parsed[0]), parsed[1] } - end - return nil - end + class CharParser < BasicParser(Token) + def initialize(@char : Char) end - class OptionalParser(T) < BasicParser(T?) - def initialize(@parser : BasicParser(T)) - 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 - def parse?(tokens, index) - if parsed = @parser.parse?(tokens, index) - return { parsed[0], parsed[1] } - end - return { nil, index } - end + class TransformParser(T, R) < BasicParser(R) + def initialize(@parser : BasicParser(T), &@block : T -> R) end - class EitherParser(T) < BasicParser(T) - def initialize(@parsers : Array(BasicParser(T))) - end + def parse?(tokens, index) + if parsed = @parser.parse?(tokens, index) + return {@block.call(parsed[0]), parsed[1]} + end + return nil + end + end - def parse?(tokens, index) - @parsers.each do |parser| - if parsed = parser.parse?(tokens, index) - return parsed - end - end - return nil - end + class OptionalParser(T) < BasicParser(T?) + def initialize(@parser : BasicParser(T)) end - class ManyParser(T) < BasicParser(Array(T)) - def initialize(@parser : BasicParser(T)) - end + def parse?(tokens, index) + if parsed = @parser.parse?(tokens, index) + return {parsed[0], parsed[1]} + end + return {nil, index} + end + end - def parse?(tokens, index) - many = [] of T - while parsed = @parser.parse?(tokens, index) - item, index = parsed - many << item - end - return { many, index } - end + class EitherParser(T) < BasicParser(T) + def initialize(@parsers : Array(BasicParser(T))) end - class DelimitedParser(T, R) < BasicParser(Array(T)) - def initialize(@parser : BasicParser(T), @delimiter : BasicParser(R)) + def parse?(tokens, index) + @parsers.each do |parser| + if parsed = parser.parse?(tokens, index) + return parsed end + end + return nil + end + 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 + class ManyParser(T) < BasicParser(Array(T)) + def initialize(@parser : BasicParser(T)) end - class NextParser(T, R) < BasicParser(Array(T | R)) - def initialize(@first : BasicParser(T), @second : BasicParser(R)) - end + def parse?(tokens, index) + many = [] of T + while parsed = @parser.parse?(tokens, index) + item, index = parsed + many << item + end + return {many, index} + end + end - def parse?(tokens, index) - first = @first.parse?(tokens, index) - return nil unless first - first_value, index = first - - second = @second.parse?(tokens, index) - return nil unless second - second_value, index = second - - array = Array(T | R).new - array << first_value << second_value - return { array, index } - end + class DelimitedParser(T, R) < BasicParser(Array(T)) + def initialize(@parser : BasicParser(T), @delimiter : BasicParser(R)) end - class PlaceholderParser(T) < BasicParser(T) - property parser : BasicParser(T)? + 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 - def initialize - @parser = nil - end - - def parse?(tokens, index) - @parser.try &.parse?(tokens, index) - end + return {array, index} end + end + + class NextParser(T, R) < BasicParser(Array(T | R)) + def initialize(@first : BasicParser(T), @second : BasicParser(R)) + end + + def parse?(tokens, index) + first = @first.parse?(tokens, index) + return nil unless first + first_value, index = first + + second = @second.parse?(tokens, index) + return nil unless second + second_value, index = second + + array = Array(T | R).new + array << first_value << second_value + return {array, index} + end + end + + class PlaceholderParser(T) < BasicParser(T) + property parser : BasicParser(T)? + + def initialize + @parser = nil + end + + def 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 bb82050..0356ab8 100644 --- a/src/chalk/print_visitor.cr +++ b/src/chalk/print_visitor.cr @@ -1,53 +1,53 @@ require "./tree.cr" module Chalk - class PrintVisitor < Visitor - def initialize - @indent = 0 - end + class PrintVisitor < Visitor + def initialize + @indent = 0 + end - def print_indent - @indent.times do - STDOUT << " " - end - end + def print_indent + @indent.times do + STDOUT << " " + end + end - def visit(id : TreeId) - print_indent - puts id.id - end + def visit(id : TreeId) + print_indent + puts id.id + end - def visit(lit : TreeLit) - print_indent - puts lit.lit - end + def visit(lit : TreeLit) + print_indent + puts lit.lit + end - def visit(op : TreeOp) - print_indent - STDOUT << "[op] " - puts op.op - @indent += 1 - end + def visit(op : TreeOp) + print_indent + STDOUT << "[op] " + puts op.op + @indent += 1 + end - def finish(op : TreeOp) - @indent -= 1 - end + def finish(op : TreeOp) + @indent -= 1 + end - def visit(function : TreeFunction) - print_indent - STDOUT << "[function] " << function.name << "( " - function.params.each do |param| - STDOUT << param << " " - end - puts ")" - @indent += 1 - end + def visit(function : TreeFunction) + print_indent + STDOUT << "[function] " << function.name << "( " + function.params.each do |param| + STDOUT << param << " " + end + puts ")" + @indent += 1 + end - def finish(function : TreeFunction) - @indent -= 1 - end + def finish(function : TreeFunction) + @indent -= 1 + end - macro forward(text, type) + macro forward(text, type) def visit(tree : {{type}}) print_indent puts {{text}} @@ -59,12 +59,12 @@ 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) - end + forward("[call]", TreeCall) + forward("[block]", TreeBlock) + forward("[var]", TreeVar) + forward("[assign]", TreeAssign) + forward("[if]", TreeIf) + forward("[while]", TreeWhile) + forward("[return]", TreeReturn) + end end diff --git a/src/chalk/tree.cr b/src/chalk/tree.cr index 6ce35d8..3bc5874 100644 --- a/src/chalk/tree.cr +++ b/src/chalk/tree.cr @@ -1,138 +1,159 @@ module Chalk - class Visitor - def visit(tree : Tree) - end - - def finish(tree : Tree) - end + class Visitor + def visit(tree : Tree) end - class Tree - def accept(v : Visitor) - v.visit(self) - v.finish(self) - end + def finish(tree : Tree) + end + end + + class Tree + def accept(v : Visitor) + v.visit(self) + v.finish(self) + end + end + + class TreeId < Tree + property id : String + + def initialize(@id : String) + end + end + + class TreeLit < Tree + property lit : Int64 + + def initialize(@lit : Int64) + end + end + + class TreeCall < Tree + property name : String + property params : Array(Tree) + + def initialize(@name : String, @params : Array(Tree)) end - class TreeId < Tree - property id : String - def initialize(@id : String) 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 + property right : Tree + + def initialize(@op : TokenType, @left : Tree, @right : Tree) end - class TreeLit < Tree - property lit : Int64 - def initialize(@lit : Int64) end + def accept(v : Visitor) + v.visit(self) + @left.accept(v) + @right.accept(v) + v.finish(self) + end + end + + class TreeBlock < Tree + def initialize(@children : Array(Tree)) end - class TreeCall < Tree - property name : String - property params : Array(Tree) + def accept(v : Visitor) + v.visit(self) + @children.each &.accept(v) + v.finish(self) + end + end - def initialize(@name : String, @params : Array(Tree)) end - def accept(v : Visitor) - v.visit(self) - @params.each &.accept(v) - v.finish(self) - end + class TreeFunction < Tree + property name : String + property params : Array(String) + property block : Tree + + def initialize(@name : String, @params : Array(String), @block : Tree) end - class TreeOp < Tree - property op : TokenType - property left : Tree - property right : Tree - def initialize(@op : TokenType, @left : Tree, @right : Tree) end + def accept(v : Visitor) + v.visit(self) + @block.accept(v) + v.finish(self) + end + end - def accept(v : Visitor) - v.visit(self) - @left.accept(v) - @right.accept(v) - v.finish(self) - end + class TreeVar < Tree + property name : String + property expr : Tree + + def initialize(@name : String, @expr : Tree) end - class TreeBlock < Tree - def initialize(@children : Array(Tree)) end + def accept(v : Visitor) + v.visit(self) + @expr.accept(v) + v.finish(self) + end + end - def accept(v : Visitor) - v.visit(self) - @children.each &.accept(v) - v.finish(self) - end + class TreeAssign < Tree + property name : String + property expr : Tree + + def initialize(@name : String, @expr : Tree) end - class TreeFunction < Tree - property name : String - property params : Array(String) - property block : Tree + def accept(v : Visitor) + v.visit(self) + @expr.accept(v) + v.finish(self) + end + end - def initialize(@name : String, @params : Array(String), @block : Tree) end - def accept(v : Visitor) - v.visit(self) - @block.accept(v) - v.finish(self) - end + class TreeIf < Tree + property condition : Tree + property block : Tree + property otherwise : Tree? + + def initialize(@condition : Tree, @block : Tree, @otherwise : Tree? = nil) end - class TreeVar < Tree - property name : String - property expr : Tree + def accept(v : Visitor) + v.visit(self) + @condition.accept(v) + @block.accept(v) + @otherwise.try &.accept(v) + v.finish(self) + end + end - def initialize(@name : String, @expr : Tree) end - def accept(v : Visitor) - v.visit(self) - @expr.accept(v) - v.finish(self) - end + class TreeWhile < Tree + property condition : Tree + property block : Tree + + def initialize(@condition : Tree, @block : Tree) end - class TreeAssign < Tree - property name : String - property expr : Tree + def accept(v : Visitor) + v.visit(self) + @condition.accept(v) + @block.accept(v) + v.finish(self) + end + end - def initialize(@name : String, @expr : Tree) end - def accept(v : Visitor) - v.visit(self) - @expr.accept(v) - v.finish(self) - end + class TreeReturn < Tree + property rvalue : Tree + + def initialize(@rvalue : Tree) end - class TreeIf < Tree - property condition : Tree - property block : Tree - property otherwise : Tree? - - def initialize(@condition : Tree, @block : Tree, @otherwise : Tree? = nil) end - def accept(v : Visitor) - v.visit(self) - @condition.accept(v) - @block.accept(v) - @otherwise.try &.accept(v) - v.finish(self) - end - end - - class TreeWhile < Tree - property condition : Tree - property block : Tree - - def initialize(@condition : Tree, @block : Tree) end - def accept(v : Visitor) - v.visit(self) - @condition.accept(v) - @block.accept(v) - v.finish(self) - end - end - - class TreeReturn < Tree - property rvalue : Tree - - def initialize(@rvalue : Tree) end - def accept(v : Visitor) - v.visit(self) - @rvalue.accept(v) - v.finish(self) - end + def accept(v : Visitor) + v.visit(self) + @rvalue.accept(v) + v.finish(self) end + end end