Skip to content

Commit

Permalink
Add parser example
Browse files Browse the repository at this point in the history
  • Loading branch information
PaddiM8 committed Oct 10, 2024
1 parent 6935f67 commit 5f1ce86
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 0 deletions.
5 changes: 5 additions & 0 deletions examples/parser/ast.elk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub struct Token(kind, value)

pub struct BinaryExpr(left, op, right)

pub struct LiteralExpr(value)
1 change: 1 addition & 0 deletions examples/parser/errors.elk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub struct ParseError(message)
24 changes: 24 additions & 0 deletions examples/parser/evaluator.elk
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using ./ast

pub fn evaluate(ast) {
next(ast)
}

fn next(expr) {
return nextBinary(expr) if expr | isType(BinaryExpr)
return nextLiteral(expr) if expr | isType(LiteralExpr)
}

fn nextBinary(expr) {
let left = expr->left | next
let right = expr->right | next

return left + right if expr->op == "+"
return left - right if expr->op == "-"
return left * right if expr->op == "*"
return left / right if expr->op == "/"
}

fn nextLiteral(expr) {
expr->value->value | into::float
}
60 changes: 60 additions & 0 deletions examples/parser/lexer.elk
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using ./errors
using ./ast

let chars = ""
let index = 0

pub fn lex(input) {
chars = input

let tokens = []
while index < len(chars) {
tokens | push(next)
}

chars = ""
index = 0

tokens
}

fn current() {
chars | iter::at(index)
}

fn next() {
let c = current()
while c | str::isWhitespace {
c = advance
}

return nil if c == nil

if c in ["+", "-", "*", "/"] {
advance
return new Token(c, c)
}

return nextNumber() if c | str::isDigit

throw new ParseError("Unexpected character: ${c}")
}

fn nextNumber() {
let digits = []
while (str::isDigit(current) or current() == ".") {
digits | push(current)
advance()
}

let numberString = digits | join

return new Token("number", into::float(numberString)) if "." in digits
return new Token("number", into::int(numberString))
}

fn advance() {
index += 1

current()
}
24 changes: 24 additions & 0 deletions examples/parser/main.elk
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
with ./parser
with ./evaluator

fn main() {
if (len(getArgv) > 1) {
getArgv
| join " "
| evaluate
| println

return
}

while true {
let input = ">> " | ansi::color blue | io::interactiveInput
continue if not input
input | evaluate | println
}
}

fn evaluate(input) {
parser::parse(input)
| evaluator::evaluate
}
66 changes: 66 additions & 0 deletions examples/parser/parser.elk
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using ./errors
using ./ast
with ./lexer

let tokens = []
let index = 0

pub fn parse(input) {
tokens = lexer::lex(input)
index = 0

expr()
}

fn current() {
tokens | iter::at(index)
}

fn advance() {
let token = current()
index += 1

token
}

fn match(kinds...) {
return false if not current()
kinds | iter::anyOf => kind { kind == current()->kind }
}

fn expr() {
additive()
}

fn additive() {
let left = multiplicative()
while match("+", "-") {
let op = advance()->kind
let right = multiplicative()

left = new BinaryExpr(left, op, right)
}

left
}

fn multiplicative() {
let left = primary()
while match("*", "/") {
let op = advance()->kind
let right = primary()

left = new BinaryExpr(left, op, right)
}

left
}

fn primary() {
if match("number") {
return new LiteralExpr(advance())
}

throw new ParseError("Unexpected end of expression") if current() != nil
throw new ParseError("Unexpected token: '${current()->kind}'")
}

0 comments on commit 5f1ce86

Please sign in to comment.