Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/lib/desugar/sugar.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def desugarStmt(s: surfaceAst::Stmt): coreAst::Stmt / { Exception[DesugarError],
val binaryExpr = operator match {
case PlusAssign() => surfaceAst::AddExpr(variableExpr, value, pos)
case MinusAssign() => surfaceAst::SubExpr(variableExpr, value, pos)
case _ => do raise(DesugarError(), "unsupported compound assignment operator in desugaring: " ++ operator.show)
case _ => do raise(DesugarError(), "unsupported compound assignment operator in desugaring: " ++ helpers::show(operator))
}
coreAst::AssignStmt(name, desugarExpr(binaryExpr), pos)
}
Expand Down
6 changes: 3 additions & 3 deletions src/lib/interpreter/eval.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ def evalExpr(expr: Expr): Value / { Context[Address], Store, Exception[RuntimeEr
if (r != 0) {
VInt(l / r)
} else {
do raise(errors::RuntimeError(), "Division by zero at " ++ do getPos().show)
do raise(errors::RuntimeError(), "Division by zero at " ++ helpers::show(do commonEffects::getPos()))
}
}
case (VDouble(l), VDouble(r)) => {
if (r != 0.0) {
VDouble(l / r)
} else {
do raise(errors::RuntimeError(), "Division by zero at " ++ do getPos().show)
do raise(errors::RuntimeError(), "Division by zero at " ++ helpers::show(do commonEffects::getPos()))
}
}
case _ => do raise(errors::RuntimeError(), "Type error in division")
Expand Down Expand Up @@ -368,7 +368,7 @@ def evalExpr(expr: Expr): Value / { Context[Address], Store, Exception[RuntimeEr

}

case _ => raise(errors::RuntimeError(), "trying to call a non-function value at " ++ pos.show)
case _ => raise(errors::RuntimeError(), "trying to call a non-function value at " ++ helpers::show(pos))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/interpreter/handlers.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def handleRuntimeContext[R](initEnv: Map[String, Address]) { prog: => R / { Cont
def lookup(name) = {
context.get(name) match {
case Some(addr) => resume(addr)
case None() => do raise(errors::RuntimeError(), "Undefined variable '" ++ name ++ "' at " ++ do getPos().show)
case None() => do raise(errors::RuntimeError(), "Undefined variable '" ++ name ++ "' at " ++ helpers::show(do commonEffects::getPos()))
}
}

Expand Down
142 changes: 71 additions & 71 deletions src/lib/lexer/lexer.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -34,50 +34,50 @@ def scanWithPos[R]() { prog: => R / { Scan[Char], Positioning } }: R / Scan[Char
}
}

try { prog() } with Scan[Char] {
def peek() = resume {
lastSeen match {
// If we already cached a peeked character, return it
case Some(c) => return c

// We rethrow the peek to the *underlying* Scan handler by calling `do peek()`.
// This ultimately causes the lower-level scanner to perform `read()`,
// which is handled by the filesystem.
//
// We intercept the returned character and store it in `lastSeen`,
// but we do NOT advance the position yet (because peek does not consume)
case None() => {
val c = do Scan::peek()
lastSeen = Some(c)
return c
}
}
}

def skip() = resume {
try { prog() } with next[Char] {
resume {
lastSeen match {
// Case 1: a previous peek has cached the next character.
// That means this skip is *consuming* exactly that character.
case Some(c) => {
// We rethrow the skip to the underlying handler (`do skip()`).
// That underlying skip will not trigger another read, since the lower
// scanner already buffered the character during peek.
do skip()
scanner::skip()
// Now we know the character was consumed → update position.
updatePos(c)
// Clear peek cache
lastSeen = None()
return ()
return c
}
// Case 2: skip happens without a prior peek.
// That means we must read the character now in order to know what is consumed
case None() => {
// We first rethrow a peek to underlying handler to obtain the character
val c = do Scan::peek()
val c = do stream::peek()
// Then we perform the actual skip (again rethrown)
updatePos(c)
do skip()
return ()
scanner::skip()
return c
}
}
}
} with peek[Char] {
resume {
lastSeen match {
// If we already cached a peeked character, return it
case Some(c) => return c

// We rethrow the peek to the *underlying* Scan handler by calling `do peek()`.
// This ultimately causes the lower-level scanner to perform `read()`,
// which is handled by the filesystem.
//
// We intercept the returned character and store it in `lastSeen`,
// but we do NOT advance the position yet (because peek does not consume)
case None() => {
val c = do stream::peek()
lastSeen = Some(c)
return c
}
}
}
Expand Down Expand Up @@ -128,7 +128,7 @@ def nextToken(): Token / { Scan[Char], stop, Exception[LexerError], Positioning
// checks if the next char is alphabetic (used to disallow identifiers starting with numbers)
def checkInvalidTrailingAlpha(): Bool / { Scan[Char], stop } = {
try {
val next = do Scan::peek()
val next = do stream::peek()
next.isAlphabetic || next == '_'
} with stop {
false
Expand All @@ -137,55 +137,55 @@ def nextToken(): Token / { Scan[Char], stop, Exception[LexerError], Positioning

// reads a decimal number (integer part only)
def readNumberToken(pos: Position): Token / { Scan[Char], stop } = {
val integerPart = try { readDecimal() } with fail { raise(LexerError(), "invalid number at " ++ pos.show) }
val next = try { do Scan::peek() } with stop { ' ' }
val integerPart = try { readDecimal() } with fail { raise(LexerError(), "invalid number at " ++ helpers::show(pos)) }
val next = try { do stream::peek() } with stop { ' ' }

// check for fractional part
if (next == '.') {
// consume '.'
do Scan::skip()
scanner::skip()

// read fractional part as string to avoid precision issues like 123.000001
val fractionalPart = string::collect {
readMany { c => c.isDigit }
}

if (fractionalPart == "") {
do raise(LexerError(), "Expected digit after '.' at " ++ pos.show)
do raise(LexerError(), "Expected digit after '.' at " ++ helpers::show(pos))
}

if (checkInvalidTrailingAlpha()) {
do raise(LexerError(), "Invalid identifier starting with number at " ++ pos.show)
do raise(LexerError(), "Invalid identifier starting with number at " ++ helpers::show(pos))
}

val doubleVal = stringToDouble(integerPart.show ++ "." ++ fractionalPart)
Token(Double(doubleVal), pos)
} else {
if (checkInvalidTrailingAlpha()) {
val rest = string::collect { readMany { c => c.isAlphanumeric } }
do raise(LexerError(), "invalid char at " ++ pos.show ++ " in " ++ "'" ++ integerPart.show ++ rest ++ "'")
do raise(LexerError(), "invalid char at " ++ helpers::show(pos) ++ " in " ++ "'" ++ integerPart.show ++ rest ++ "'")
}
Token(Integer(integerPart), pos)
}
}

def readStringLikeToken(pos: Position, delim: Char): Token / { Scan[Char], stop, Exception[LexerError] } = {
// consume opening "
do Scan::skip()
scanner::skip()

// collect string content, handling escape sequences
val strValue = try {
string::collect {

def loop(): Unit = {
val c = do Scan::peek()
val c = do stream::peek()

if (c == delim) {
() // end of string literal, stop collecting
} else if (c == '\\') {
do Scan::skip() // consume the \
val escaped = do Scan::peek()
do Scan::skip() // consume the char after the \
scanner::skip() // consume the \
val escaped = do stream::peek()
scanner::skip() // consume the char after the \

// handle escape sequences
escaped match {
Expand All @@ -202,20 +202,20 @@ def nextToken(): Token / { Scan[Char], stop, Exception[LexerError], Positioning
}
loop()
} else {
do Scan::skip()
scanner::skip()
do emit(c) // normal char
loop()
}
}
loop()
}
} with stop {
do raise(LexerError(), "unterminated string literal at " ++ pos.show)
do raise(LexerError(), "unterminated string literal at " ++ helpers::show(pos))
}

// expect closing "
if (do Scan::peek() == delim) {
do Scan::skip()
if (do stream::peek() == delim) {
scanner::skip()
} else {
do raise(LexerError(), "Expected " ++ delim.show ++ " at end of string")
}
Expand All @@ -226,10 +226,10 @@ def nextToken(): Token / { Scan[Char], stop, Exception[LexerError], Positioning
// always skip whitespace
skipWhitespace()

val pos = do getPos()
val pos = do commonEffects::getPos()

// one char lookahead
val c = do Scan::peek()
val c = do stream::peek()

if (c.isAlphabetic) {
readIdentifierOrKeyword(pos)
Expand All @@ -241,86 +241,86 @@ def nextToken(): Token / { Scan[Char], stop, Exception[LexerError], Positioning
readStringLikeToken(pos, '\'')
} else if (c == '=') {
// consume first char
do Scan::skip()
scanner::skip()
// second lookahead to recognize two char tokens
val next = do Scan::peek()
val next = do stream::peek()

if(next == '=') {
do Scan::skip()
scanner::skip()
Token(Equal(), pos)
} else {
Token(Assign(), pos)
}
} else if (c == '!') {
// consume '!' and check for '!='
do Scan::skip()
scanner::skip()
readIf('=')
Token(NotEqual(), pos)
} else if (c == '<') {
do Scan::skip()
val next = do Scan::peek()
scanner::skip()
val next = do stream::peek()
if (next == '=') {
do Scan::skip()
scanner::skip()
Token(LessEqual(), pos)
} else {
Token(Less(), pos)
}
} else if (c == '>') {
do Scan::skip()
val next = do Scan::peek()
scanner::skip()
val next = do stream::peek()
if (next == '=') {
do Scan::skip()
scanner::skip()
Token(GreaterEqual(), pos)
} else {
Token(Greater(), pos)
}
} else if (c == '+') {
do Scan::skip()
val next = do Scan::peek()
scanner::skip()
val next = do stream::peek()
if (next == '=') {
do Scan::skip()
scanner::skip()
Token(PlusAssign(), pos)
} else {
Token(Plus(), pos)
}
} else if (c == '-') {
do Scan::skip()
val next = do Scan::peek()
scanner::skip()
val next = do stream::peek()
if (next == '=') {
do Scan::skip()
scanner::skip()
Token(MinusAssign(), pos)
} else if (next == '>') {
do Scan::skip()
scanner::skip()
Token(Arrow(), pos)
} else {
Token(Minus(), pos)
}
} else if (c == '*') {
do Scan::skip()
scanner::skip()
Token(Mult(), pos)
} else if (c == '/') {
do Scan::skip()
scanner::skip()
Token(Div(), pos)
} else if (c == '(') {
do Scan::skip()
scanner::skip()
Token(LParen(), pos)
} else if (c == ')') {
do Scan::skip()
scanner::skip()
Token(RParen(), pos)
} else if (c == '{') {
do Scan::skip()
scanner::skip()
Token(LBrace(), pos)
} else if (c == '}') {
do Scan::skip()
scanner::skip()
Token(RBrace(), pos)
} else if (c == ':') {
do Scan::skip()
scanner::skip()
Token(Colon(), pos)
} else if (c == ',') {
do Scan::skip()
scanner::skip()
Token(Comma(), pos)
} else {
do raise(LexerError(), "can not tokenize char: " ++ "'" ++ c.show ++ "'" ++ " at " ++ pos.show)
do raise(LexerError(), "can not tokenize char: " ++ "'" ++ c.show ++ "'" ++ " at " ++ helpers::show(pos))
}
}

Expand All @@ -335,7 +335,7 @@ def lexer[R]() { program: => R / Lexer[Token] }: R / { Scan[Char], Positioning,
try {
nextToken()
} with stop {
Token(EOF(), do getPos())
Token(EOF(), do commonEffects::getPos())
}
}

Expand Down
Loading
Loading