188 lines
7.4 KiB
Nim
188 lines
7.4 KiB
Nim
# Builtins
|
|
import sequtils
|
|
import strutils
|
|
import strformat
|
|
|
|
# Our stuff
|
|
import frontend/lexer as l
|
|
import frontend/parser as p
|
|
|
|
# Thanks art <3
|
|
import jale/editor as ed
|
|
import jale/templates
|
|
import jale/plugin/defaults
|
|
import jale/plugin/editor_history
|
|
import jale/keycodes
|
|
import jale/multiline
|
|
|
|
|
|
# Forward declarations
|
|
proc fillSymbolTable(tokenizer: Lexer)
|
|
proc getLineEditor: LineEditor
|
|
|
|
|
|
when isMainModule:
|
|
setControlCHook(proc () {.noconv.} = quit(0))
|
|
var keep = true
|
|
var tokens: seq[Token] = @[]
|
|
var tokenizer = newLexer()
|
|
var parser = newParser()
|
|
let editor = getLineEditor()
|
|
var input: string
|
|
editor.bindEvent(jeQuit):
|
|
keep = false
|
|
editor.bindKey("ctrl+a"):
|
|
editor.content.home()
|
|
editor.bindKey("ctrl+e"):
|
|
editor.content.`end`()
|
|
tokenizer.fillSymbolTable()
|
|
while keep:
|
|
try:
|
|
input = editor.read()
|
|
if input.len() > 0:
|
|
# Currently the parser doesn't handle these tokens well
|
|
tokens = filter(tokenizer.lex(input, "<stdin>"), proc (x: Token): bool = x.kind notin {TokenType.Whitespace, Tab})
|
|
echo "Tokenization step:"
|
|
for i, token in tokens:
|
|
if i == tokens.high():
|
|
# Who cares about EOF?
|
|
break
|
|
echo "\t", token
|
|
echo ""
|
|
echo "Parsing step:"
|
|
for node in parser.parse(tokens, "<stdin>"):
|
|
echo "\t", node
|
|
except IOError:
|
|
break
|
|
except LexingError:
|
|
let lineNo = tokenizer.getLine()
|
|
let relPos = tokenizer.getRelPos(lineNo)
|
|
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip()
|
|
echo getCurrentExceptionMsg()
|
|
echo &"Source line: {line}"
|
|
echo " ".repeat(relPos.start + len("Source line: ")) & "^".repeat(relPos.stop - relPos.start)
|
|
except ParseError:
|
|
let lineNo = parser.getCurrentToken().line
|
|
let relPos = tokenizer.getRelPos(lineNo)
|
|
let line = tokenizer.getSource().splitLines()[lineNo - 1].strip()
|
|
echo getCurrentExceptionMsg()
|
|
echo &"Source line: {line}"
|
|
echo " ".repeat(relPos.start + len("Source line: ")) & "^".repeat(relPos.stop - parser.getCurrentToken().lexeme.len())
|
|
quit(0)
|
|
|
|
|
|
|
|
proc fillSymbolTable(tokenizer: Lexer) =
|
|
## Initializes the Lexer's symbol
|
|
## table with the builtin symbols
|
|
## and keywords
|
|
|
|
# 1-byte symbols
|
|
tokenizer.symbols.addSymbol("`", Backtick)
|
|
tokenizer.symbols.addSymbol("+", Plus)
|
|
tokenizer.symbols.addSymbol("-", Minus)
|
|
tokenizer.symbols.addSymbol("*", Star)
|
|
tokenizer.symbols.addSymbol("/", Slash)
|
|
tokenizer.symbols.addSymbol("{", LeftBrace)
|
|
tokenizer.symbols.addSymbol("}", RightBrace)
|
|
tokenizer.symbols.addSymbol("(", LeftParen)
|
|
tokenizer.symbols.addSymbol(")", RightParen)
|
|
tokenizer.symbols.addSymbol("[", LeftBracket)
|
|
tokenizer.symbols.addSymbol("]", RightBracket)
|
|
tokenizer.symbols.addSymbol(".", Dot)
|
|
tokenizer.symbols.addSymbol(",", Comma)
|
|
tokenizer.symbols.addSymbol(">", GreaterThan)
|
|
tokenizer.symbols.addSymbol("<", LessThan)
|
|
tokenizer.symbols.addSymbol(";", Semicolon)
|
|
tokenizer.symbols.addSymbol("=", Equal)
|
|
tokenizer.symbols.addSymbol("~", Tilde)
|
|
tokenizer.symbols.addSymbol("%", Percentage)
|
|
tokenizer.symbols.addSymbol(":", Colon)
|
|
tokenizer.symbols.addSymbol("&", Ampersand)
|
|
tokenizer.symbols.addSymbol("^", Caret)
|
|
tokenizer.symbols.addSymbol("|", Pipe)
|
|
# 2-byte symbols
|
|
tokenizer.symbols.addSymbol("+=", InplaceAdd)
|
|
tokenizer.symbols.addSymbol("-=", InplaceSub)
|
|
tokenizer.symbols.addSymbol(">=", GreaterOrEqual)
|
|
tokenizer.symbols.addSymbol("<=", LessOrEqual)
|
|
tokenizer.symbols.addSymbol("*=", InplaceMul)
|
|
tokenizer.symbols.addSymbol("/=", InplaceDiv)
|
|
tokenizer.symbols.addSymbol("&=", InplaceAnd)
|
|
tokenizer.symbols.addSymbol("!=", NotEqual)
|
|
tokenizer.symbols.addSymbol("|=", InplaceOr)
|
|
tokenizer.symbols.addSymbol("^=", InplaceXor)
|
|
tokenizer.symbols.addSymbol("%=", InplaceMod)
|
|
tokenizer.symbols.addSymbol("//", FloorDiv)
|
|
tokenizer.symbols.addSymbol("==", DoubleEqual)
|
|
tokenizer.symbols.addSymbol("**", DoubleStar)
|
|
tokenizer.symbols.addSymbol(">>", RightShift)
|
|
tokenizer.symbols.addSymbol("<<", LeftShift)
|
|
# 3-byte symbols
|
|
tokenizer.symbols.addSymbol("//=", InplaceFloorDiv)
|
|
tokenizer.symbols.addSymbol("**=", InplacePow)
|
|
tokenizer.symbols.addSymbol(">>=", InplaceRightShift)
|
|
tokenizer.symbols.addSymbol("<<=", InplaceLeftShift)
|
|
# Keywords
|
|
tokenizer.symbols.addKeyword("type", Type)
|
|
tokenizer.symbols.addKeyword("enum", Enum)
|
|
tokenizer.symbols.addKeyword("case", Case)
|
|
tokenizer.symbols.addKeyword("operator", Operator)
|
|
tokenizer.symbols.addKeyword("generator", Generator)
|
|
tokenizer.symbols.addKeyword("function", Function)
|
|
tokenizer.symbols.addKeyword("coroutine", Coroutine)
|
|
tokenizer.symbols.addKeyword("break", Break)
|
|
tokenizer.symbols.addKeyword("continue", Continue)
|
|
tokenizer.symbols.addKeyword("while", While)
|
|
tokenizer.symbols.addKeyword("for", For)
|
|
tokenizer.symbols.addKeyword("foreach", Foreach)
|
|
tokenizer.symbols.addKeyword("if", If)
|
|
tokenizer.symbols.addKeyword("else", Else)
|
|
tokenizer.symbols.addKeyword("await", Await)
|
|
tokenizer.symbols.addKeyword("defer", Defer)
|
|
tokenizer.symbols.addKeyword("try", Try)
|
|
tokenizer.symbols.addKeyword("except", Except)
|
|
tokenizer.symbols.addKeyword("finally", Finally)
|
|
tokenizer.symbols.addKeyword("raise", Raise)
|
|
tokenizer.symbols.addKeyword("assert", Assert)
|
|
tokenizer.symbols.addKeyword("const", Const)
|
|
tokenizer.symbols.addKeyword("let", Let)
|
|
tokenizer.symbols.addKeyword("var", Var)
|
|
tokenizer.symbols.addKeyword("import", Import)
|
|
tokenizer.symbols.addKeyword("yield", Yield)
|
|
tokenizer.symbols.addKeyword("return", Return)
|
|
# These are technically more like expressions
|
|
# with a reserved name that produce a value of a
|
|
# builtin type, but we don't need to care about
|
|
# that until we're in the parsing and compilation
|
|
# steps so it's fine
|
|
tokenizer.symbols.addKeyword("nan", NotANumber)
|
|
tokenizer.symbols.addKeyword("inf", Infinity)
|
|
tokenizer.symbols.addKeyword("nil", Nil)
|
|
tokenizer.symbols.addKeyword("true", True)
|
|
tokenizer.symbols.addKeyword("false", False)
|
|
# These are technically operators, but since
|
|
# they fit neatly into the definition for an
|
|
# identifier/keyword we parse them as such
|
|
# and specialize them later
|
|
tokenizer.symbols.addKeyword("isnot", IsNot)
|
|
tokenizer.symbols.addKeyword("is", Is)
|
|
tokenizer.symbols.addKeyword("as", As)
|
|
tokenizer.symbols.addKeyword("of", Of)
|
|
tokenizer.symbols.addKeyword("and", LogicalAnd)
|
|
tokenizer.symbols.addKeyword("or", LogicalOr)
|
|
tokenizer.symbols.addKeyword("not", LogicalNot)
|
|
|
|
# P.S.: There's no reason for the order of addition of
|
|
# symbols to be ascending in length (the symbol table uses
|
|
# a hashmap internally). You can add/remove symbols (and
|
|
# keywords for that matter) as you like!
|
|
|
|
|
|
proc getLineEditor: LineEditor =
|
|
result = newLineEditor()
|
|
result.prompt = "=> "
|
|
result.populateDefaults() # Setup default keybindings
|
|
let hist = result.plugHistory() # Create history object
|
|
result.bindHistory(hist) # Set default history keybindings
|