Initial commit
This is the state of this project as it was back at the end of March, when I stopped working on it. I left markdown files as documentation for myself of the stuff I wanted to do.
This commit is contained in:
commit
7c1ce16f4b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
__pycache__
|
||||||
|
.mypy_cache
|
14
TODO.md
Normal file
14
TODO.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# TODO:
|
||||||
|
- Evaluate values knowing their types
|
||||||
|
|
||||||
|
- Generics
|
||||||
|
- Syntax sugar for +=
|
||||||
|
- Unions
|
||||||
|
- Matching unions (maybe do match cases with an 'as' keyword + type after each case)
|
||||||
|
- Dictionaries
|
||||||
|
- Preprocessor?
|
||||||
|
- Maybe have breaks and continues as values.
|
||||||
|
|
||||||
|
# DONE
|
||||||
|
- Scopes & contexts
|
||||||
|
- Importing
|
11
dict.ppp
Normal file
11
dict.ppp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
func doubles(a: int) -> int -> int {
|
||||||
|
assert a == 2;
|
||||||
|
func doubler(b: int) -> int {
|
||||||
|
return b*2+a;
|
||||||
|
}
|
||||||
|
return doubler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
doubler_: int -> int = doubles(2);
|
||||||
|
debug_print(doubler_("5"));
|
874
ppp-whole.ppp
Normal file
874
ppp-whole.ppp
Normal file
@ -0,0 +1,874 @@
|
|||||||
|
enum Keyword {
|
||||||
|
Enum,
|
||||||
|
Struct,
|
||||||
|
Func,
|
||||||
|
If,
|
||||||
|
Else,
|
||||||
|
While,
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Do,
|
||||||
|
For,
|
||||||
|
To,
|
||||||
|
In,
|
||||||
|
Match,
|
||||||
|
Case,
|
||||||
|
Assert,
|
||||||
|
Return,
|
||||||
|
Lambda
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalKeyword {
|
||||||
|
Some(Keyword),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyword_from_str(keyword: str) -> OptionalKeyword {
|
||||||
|
if keyword == "enum" return OptionalKeyword.Some(Keyword.Enum);
|
||||||
|
if keyword == "struct" return OptionalKeyword.Some(Keyword.Struct);
|
||||||
|
if keyword == "func" return OptionalKeyword.Some(Keyword.Func);
|
||||||
|
if keyword == "if" return OptionalKeyword.Some(Keyword.If);
|
||||||
|
if keyword == "else" return OptionalKeyword.Some(Keyword.Else);
|
||||||
|
if keyword == "while" return OptionalKeyword.Some(Keyword.While);
|
||||||
|
if keyword == "break" return OptionalKeyword.Some(Keyword.Break);
|
||||||
|
if keyword == "continue" return OptionalKeyword.Some(Keyword.Continue);
|
||||||
|
if keyword == "do" return OptionalKeyword.Some(Keyword.Do);
|
||||||
|
if keyword == "for" return OptionalKeyword.Some(Keyword.For);
|
||||||
|
if keyword == "to" return OptionalKeyword.Some(Keyword.To);
|
||||||
|
if keyword == "in" return OptionalKeyword.Some(Keyword.In);
|
||||||
|
if keyword == "match" return OptionalKeyword.Some(Keyword.Match);
|
||||||
|
if keyword == "case" return OptionalKeyword.Some(Keyword.Case);
|
||||||
|
if keyword == "assert" return OptionalKeyword.Some(Keyword.Assert);
|
||||||
|
if keyword == "return" return OptionalKeyword.Some(Keyword.Return);
|
||||||
|
if keyword == "lambda" return OptionalKeyword.Some(Keyword.Lambda);
|
||||||
|
return OptionalKeyword.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyword_to_str(keyword: Keyword) -> str {
|
||||||
|
match keyword in {
|
||||||
|
case Enum return "enum";
|
||||||
|
case Struct return "struct";
|
||||||
|
case Func return "func";
|
||||||
|
case If return "if";
|
||||||
|
case Else return "else";
|
||||||
|
case While return "while";
|
||||||
|
case Break return "break";
|
||||||
|
case Continue return "continue";
|
||||||
|
case Do return "do";
|
||||||
|
case For return "for";
|
||||||
|
case To return "to";
|
||||||
|
case In return "in";
|
||||||
|
case Match return "match";
|
||||||
|
case Case return "case";
|
||||||
|
case Assert return "assert";
|
||||||
|
case Return return "return";
|
||||||
|
case Lambda return "lambda";
|
||||||
|
}
|
||||||
|
assert false, "Invalid keyword";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Symbol {
|
||||||
|
Open,
|
||||||
|
Close,
|
||||||
|
OpenCurly,
|
||||||
|
CloseCurly,
|
||||||
|
Comma,
|
||||||
|
OpenSquare,
|
||||||
|
CloseSquare,
|
||||||
|
Colon,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Arrow,
|
||||||
|
Semicolon,
|
||||||
|
Equal,
|
||||||
|
Dequal,
|
||||||
|
Exclamation,
|
||||||
|
NotEqual,
|
||||||
|
Dot,
|
||||||
|
Plus,
|
||||||
|
Dash,
|
||||||
|
Asterisk,
|
||||||
|
Dasterisk,
|
||||||
|
Slash,
|
||||||
|
QuestionMark,
|
||||||
|
Ampersand,
|
||||||
|
Dampersand,
|
||||||
|
Pipe,
|
||||||
|
Dpipe,
|
||||||
|
Dleft,
|
||||||
|
Dright,
|
||||||
|
GreaterEqual,
|
||||||
|
LesserEqual,
|
||||||
|
Percent,
|
||||||
|
Tilde,
|
||||||
|
Carot
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalSymbol {
|
||||||
|
Some(Symbol),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func symbol_from_str(symbol: str) -> OptionalSymbol {
|
||||||
|
if symbol == "(" return OptionalSymbol.Some(Symbol.Open);
|
||||||
|
if symbol == ")" return OptionalSymbol.Some(Symbol.Close);
|
||||||
|
if symbol == "{" return OptionalSymbol.Some(Symbol.OpenCurly);
|
||||||
|
if symbol == "}" return OptionalSymbol.Some(Symbol.CloseCurly);
|
||||||
|
if symbol == "," return OptionalSymbol.Some(Symbol.Comma);
|
||||||
|
if symbol == "[" return OptionalSymbol.Some(Symbol.OpenSquare);
|
||||||
|
if symbol == "]" return OptionalSymbol.Some(Symbol.CloseSquare);
|
||||||
|
if symbol == ":" return OptionalSymbol.Some(Symbol.Colon);
|
||||||
|
if symbol == "<" return OptionalSymbol.Some(Symbol.Left);
|
||||||
|
if symbol == ">" return OptionalSymbol.Some(Symbol.Right);
|
||||||
|
if symbol == "->" return OptionalSymbol.Some(Symbol.Arrow);
|
||||||
|
if symbol == ";" return OptionalSymbol.Some(Symbol.Semicolon);
|
||||||
|
if symbol == "=" return OptionalSymbol.Some(Symbol.Equal);
|
||||||
|
if symbol == "==" return OptionalSymbol.Some(Symbol.Dequal);
|
||||||
|
if symbol == "!" return OptionalSymbol.Some(Symbol.Exclamation);
|
||||||
|
if symbol == "!=" return OptionalSymbol.Some(Symbol.NotEqual);
|
||||||
|
if symbol == "." return OptionalSymbol.Some(Symbol.Dot);
|
||||||
|
if symbol == "+" return OptionalSymbol.Some(Symbol.Plus);
|
||||||
|
if symbol == "-" return OptionalSymbol.Some(Symbol.Dash);
|
||||||
|
if symbol == "*" return OptionalSymbol.Some(Symbol.Asterisk);
|
||||||
|
if symbol == "**" return OptionalSymbol.Some(Symbol.Dasterisk);
|
||||||
|
if symbol == "/" return OptionalSymbol.Some(Symbol.Slash);
|
||||||
|
if symbol == "?" return OptionalSymbol.Some(Symbol.QuestionMark);
|
||||||
|
if symbol == "&" return OptionalSymbol.Some(Symbol.Ampersand);
|
||||||
|
if symbol == "&&" return OptionalSymbol.Some(Symbol.Dampersand);
|
||||||
|
if symbol == "|" return OptionalSymbol.Some(Symbol.Pipe);
|
||||||
|
if symbol == "||" return OptionalSymbol.Some(Symbol.Dpipe);
|
||||||
|
if symbol == "<<" return OptionalSymbol.Some(Symbol.Dleft);
|
||||||
|
if symbol == ">>" return OptionalSymbol.Some(Symbol.Dright);
|
||||||
|
if symbol == ">=" return OptionalSymbol.Some(Symbol.GreaterEqual);
|
||||||
|
if symbol == "<=" return OptionalSymbol.Some(Symbol.LesserEqual);
|
||||||
|
if symbol == "%" return OptionalSymbol.Some(Symbol.Percent);
|
||||||
|
if symbol == "~" return OptionalSymbol.Some(Symbol.Tilde);
|
||||||
|
if symbol == "^" return OptionalSymbol.Some(Symbol.Carot);
|
||||||
|
assert false, "Unimplemented symbol '%s'" % symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
func symbol_to_str(symbol: Symbol) -> str {
|
||||||
|
match symbol in {
|
||||||
|
case Open return "(";
|
||||||
|
case Close return ")";
|
||||||
|
case OpenCurly return "{";
|
||||||
|
case CloseCurly return "}";
|
||||||
|
case Comma return ",";
|
||||||
|
case OpenSquare return "[";
|
||||||
|
case CloseSquare return "]";
|
||||||
|
case Colon return ":";
|
||||||
|
case Left return "<";
|
||||||
|
case Right return ">";
|
||||||
|
case Arrow return "->";
|
||||||
|
case Semicolon return ";";
|
||||||
|
case Equal return "=";
|
||||||
|
case Dequal return "==";
|
||||||
|
case Exclamation return "!";
|
||||||
|
case NotEqual return "!=";
|
||||||
|
case Dot return ".";
|
||||||
|
case Plus return "+";
|
||||||
|
case Dash return "-";
|
||||||
|
case Asterisk return "*";
|
||||||
|
case Dasterisk return "**";
|
||||||
|
case Slash return "/";
|
||||||
|
case QuestionMark return "?";
|
||||||
|
case Ampersand return "&";
|
||||||
|
case Dampersand return "&&";
|
||||||
|
case Pipe return "|";
|
||||||
|
case Dpipe return "||";
|
||||||
|
case Dleft return "<<";
|
||||||
|
case Dright return ">>";
|
||||||
|
case GreaterEqual return ">=";
|
||||||
|
case LesserEqual return "<=";
|
||||||
|
case Percent return "%";
|
||||||
|
case Tilde return "~";
|
||||||
|
case Carot return "^";
|
||||||
|
}
|
||||||
|
assert false, "Invalid symbol";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TokenContents {
|
||||||
|
Keyword(Keyword),
|
||||||
|
Identifier(str),
|
||||||
|
Number(int),
|
||||||
|
String(str),
|
||||||
|
Symbol(Symbol),
|
||||||
|
Eof
|
||||||
|
}
|
||||||
|
|
||||||
|
func token_contents_to_str(token: TokenContents) -> str {
|
||||||
|
match token in {
|
||||||
|
case Keyword(keyword) return "Keyword(%s)" % keyword_to_str(keyword);
|
||||||
|
case Identifier(string) return "Identifier(%s)" % string;
|
||||||
|
case Number(number) return "Number(%d)" % number;
|
||||||
|
case String(string) return "String(\"%s\")" % string;
|
||||||
|
case Symbol(symbol) return "Symbol('%s')" % symbol_to_str(symbol);
|
||||||
|
case Eof return "Eof";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Token {
|
||||||
|
line: int,
|
||||||
|
col: int,
|
||||||
|
value: str,
|
||||||
|
contents: TokenContents
|
||||||
|
}
|
||||||
|
|
||||||
|
func token_to_str(token: Token) -> str {
|
||||||
|
return token_contents_to_str(token.contents)+"{:"+int_to_str(token.line)+":"+int_to_str(token.col)+"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalToken {
|
||||||
|
Some(Token),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_some_token(maybe_token: OptionalToken) -> bool {
|
||||||
|
match maybe_token in {
|
||||||
|
case Some(_) return true;
|
||||||
|
case None return false;
|
||||||
|
}
|
||||||
|
assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Lexer {
|
||||||
|
source: str,
|
||||||
|
location: int,
|
||||||
|
line: int,
|
||||||
|
col: int,
|
||||||
|
peeked_token: OptionalToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func new_lexer(source: str) -> Lexer {
|
||||||
|
return Lexer{
|
||||||
|
source = source,
|
||||||
|
location = 0,
|
||||||
|
line = 1,
|
||||||
|
col = 0,
|
||||||
|
peeked_token = OptionalToken.None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_from_file(path: str) -> Lexer return new_lexer(read(path));
|
||||||
|
|
||||||
|
func is_space(char: str) -> bool {
|
||||||
|
return char == " " || char == "\t" || char == "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_digit(char: str) -> bool {
|
||||||
|
return char == "0" || char == "1" || char == "2" || char == "3" || char == "4" || char == "5" || char == "6" || char == "7" || char == "8" || char == "9";
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_alpha(char: str) -> bool {
|
||||||
|
return char == "a" || char == "b" || char == "c" || char == "d" || char == "e" || char == "f" || char == "g" || char == "h" || char == "i" || char == "j" || char == "k" || char == "l" || char == "m" || char == "n" || char == "o" || char == "p" || char == "q" || char == "r" || char == "s" || char == "t" || char == "u" || char == "v" || char == "w" || char == "x" || char == "y" || char == "z" || char == "A" || char == "B" || char == "C" || char == "D" || char == "E" || char == "F" || char == "G" || char == "H" || char == "I" || char == "J" || char == "K" || char == "L" || char == "M" || char == "N" || char == "O" || char == "P" || char == "Q" || char == "R" || char == "S" || char == "T" || char == "U" || char == "V" || char == "W" || char == "X" || char == "Y" || char == "Z" || char == "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_next_token(lexer: Lexer) -> Token {
|
||||||
|
match lexer.peeked_token in {
|
||||||
|
case Some(token) do {
|
||||||
|
lexer.peeked_token = OptionalToken.None;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while lexer.location < len(lexer.source) && is_space(lexer.source[lexer.location]) do {
|
||||||
|
if lexer.source[lexer.location] == "\n" do {
|
||||||
|
lexer.line = lexer.line + 1;
|
||||||
|
lexer.col = 0;
|
||||||
|
}
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if lexer.location >= len(lexer.source) return Token{line=lexer.line, col=lexer.col, value="\0", contents=TokenContents.Eof};
|
||||||
|
|
||||||
|
if is_digit(lexer.source[lexer.location]) do {
|
||||||
|
number_str: str = "";
|
||||||
|
while lexer.location < len(lexer.source) && is_digit(lexer.source[lexer.location]) do {
|
||||||
|
number_str = number_str + lexer.source[lexer.location];
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
number: int = str_to_int(number_str);
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value=number_str, contents=TokenContents.Number(number)};
|
||||||
|
} else if is_alpha(lexer.source[lexer.location]) do {
|
||||||
|
word_str: str = "";
|
||||||
|
while lexer.location < len(lexer.source) && is_alpha(lexer.source[lexer.location]) do {
|
||||||
|
word_str = word_str + lexer.source[lexer.location];
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
match keyword_from_str(word_str) in {
|
||||||
|
case Some(keyword) return Token{line=lexer.line, col=lexer.col, value=word_str, contents=TokenContents.Keyword(keyword)};
|
||||||
|
case None return Token{line=lexer.line, col=lexer.col, value=word_str, contents=TokenContents.Identifier(word_str)};
|
||||||
|
}
|
||||||
|
assert false, "Identifier";
|
||||||
|
} else if lexer.source[lexer.location] == "\"" do {
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
string_str: str = "";
|
||||||
|
escaping: bool = false;
|
||||||
|
while lexer.location < len(lexer.source) && (lexer.source[lexer.location] != "\"" || escaping) do {
|
||||||
|
escaping = escaping? false: lexer.source[lexer.location] == "\\";
|
||||||
|
string_str = string_str + lexer.source[lexer.location];
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="\""+string_str+"\"", contents=TokenContents.String(string_str)};
|
||||||
|
} else if lexer.source[lexer.location] == "|" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "|" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="||", contents=TokenContents.Symbol(Symbol.Dpipe)};
|
||||||
|
} else if lexer.source[lexer.location] == "&" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "&" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="&&", contents=TokenContents.Symbol(Symbol.Dampersand)};
|
||||||
|
} else if lexer.source[lexer.location] == "*" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "*" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="**", contents=TokenContents.Symbol(Symbol.Dasterisk)};
|
||||||
|
} else if lexer.source[lexer.location] == "-" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == ">" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="->", contents=TokenContents.Symbol(Symbol.Arrow)};
|
||||||
|
} else if lexer.source[lexer.location] == ">" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value=">=", contents=TokenContents.Symbol(Symbol.GreaterEqual)};
|
||||||
|
} else if lexer.source[lexer.location] == "<" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="<=", contents=TokenContents.Symbol(Symbol.LesserEqual)};
|
||||||
|
} else if lexer.source[lexer.location] == "=" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="==", contents=TokenContents.Symbol(Symbol.Dequal)};
|
||||||
|
} else if lexer.source[lexer.location] == "!" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="!=", contents=TokenContents.Symbol(Symbol.NotEqual)};
|
||||||
|
} else {
|
||||||
|
match symbol_from_str(lexer.source[lexer.location]) in {
|
||||||
|
case Some(symbol) do {
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value=lexer.source[lexer.location-1], contents=TokenContents.Symbol(symbol)};
|
||||||
|
}
|
||||||
|
case None assert False, "Unimplemented, '%s'" % lexer.source[lexer.location];
|
||||||
|
}
|
||||||
|
assert false, "Unreachable Symbol";
|
||||||
|
}
|
||||||
|
|
||||||
|
assert false, "Unreachable, next_token";
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_peek_token(lexer: Lexer) -> Token {
|
||||||
|
match lexer.peeked_token in {
|
||||||
|
case Some(token) return token;
|
||||||
|
case None do {
|
||||||
|
token: Token = lexer_next_token(lexer);
|
||||||
|
lexer.peeked_token = OptionalToken.Some(token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_check_token(lexer: Lexer, expected: TokenContents) -> bool {
|
||||||
|
token: Token = lexer_peek_token(lexer);
|
||||||
|
return token.contents == expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_take_token(lexer: Lexer, token: TokenContents) -> OptionalToken {
|
||||||
|
if lexer_check_token(lexer, token) return OptionalToken.Some(lexer_next_token(lexer));
|
||||||
|
return OptionalToken.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_take_tokens(lexer: Lexer, tokens: TokenContents[]) -> OptionalToken {
|
||||||
|
for token in tokens do {
|
||||||
|
if lexer_check_token(lexer, token) return OptionalToken.Some(lexer_next_token(lexer));
|
||||||
|
}
|
||||||
|
return OptionalToken.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_assert_token(lexer: Lexer, expected: TokenContents) -> Token {
|
||||||
|
token: Token = lexer_next_token(lexer);
|
||||||
|
assert token.contents == expected, "Expected %s but got %s!" % (token_contents_to_str(expected), token_to_str(token));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_check_tokens(lexer: Lexer, tokens: TokenContents[]) -> bool {
|
||||||
|
for token in tokens if lexer_check_token(lexer, token) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TypeExpression {
|
||||||
|
Tuple(TypeExpression[]),
|
||||||
|
Union(TypeExpression[]),
|
||||||
|
List(TypeExpression),
|
||||||
|
Array(TypeExpression, int),
|
||||||
|
Name(str),
|
||||||
|
Specification(TypeExpression, TypeExpression[]),
|
||||||
|
Function(TypeExpression[], TypeExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalTypeExpression {
|
||||||
|
Some(TypeExpression),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_type_primary(lexer: Lexer) -> TypeExpression {
|
||||||
|
base_type: TypeExpression;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Open))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) return TypeExpression.Tuple([]);
|
||||||
|
|
||||||
|
types: TypeExpression[] = [parse_type(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) types = types + [parse_type(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
base_type = TypeExpression.Tuple(types);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenSquare))) do {
|
||||||
|
assert false, "Unimplemented parse_type_primary array";
|
||||||
|
} else {
|
||||||
|
base_type = TypeExpression.Name(parse_identifier(lexer));
|
||||||
|
}
|
||||||
|
|
||||||
|
closing: Symbol;
|
||||||
|
while lexer_check_tokens(lexer, [TokenContents.Symbol(Symbol.OpenSquare), TokenContents.Symbol(Symbol.Left)]) do {
|
||||||
|
match lexer_next_token(lexer).contents in {
|
||||||
|
case Symbol(symbol) do {
|
||||||
|
match symbol in {
|
||||||
|
case OpenSquare match lexer_peek_token(lexer).contents in {
|
||||||
|
case Number(number) do {
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_type = TypeExpression.Array(base_type, number);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match symbol in {
|
||||||
|
case OpenSquare closing = Symbol.CloseSquare;
|
||||||
|
case Left closing = Symbol.Right;
|
||||||
|
case _ assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
|
||||||
|
match symbol in {
|
||||||
|
case OpenSquare if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(closing))) do {
|
||||||
|
base_type = TypeExpression.List(base_type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generics: TypeExpression[] = [parse_type(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) generics = generics + [parse_type(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(closing));
|
||||||
|
match base_type in {
|
||||||
|
case Specification assert false, "Cannot specify an already specified type";
|
||||||
|
}
|
||||||
|
|
||||||
|
base_type = TypeExpression.Specification(base_type, generics);
|
||||||
|
}
|
||||||
|
case _ assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_type(lexer: Lexer) -> TypeExpression {
|
||||||
|
base_type: TypeExpression = parse_type_primary(lexer);
|
||||||
|
if !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Arrow))) return base_type;
|
||||||
|
return_type: TypeExpression = parse_type(lexer);
|
||||||
|
match base_type in {
|
||||||
|
case Tuple(type_expressions) return TypeExpression.Function(type_expressions, return_type);
|
||||||
|
}
|
||||||
|
return TypeExpression.Function([base_type], return_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TypeDeclaration {
|
||||||
|
name: str,
|
||||||
|
type_: TypeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_type_declaration(lexer: Lexer) -> TypeDeclaration {
|
||||||
|
entry_name: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Colon));
|
||||||
|
entry_type: TypeExpression = parse_type(lexer);
|
||||||
|
return TypeDeclaration{name=entry_name, type_=entry_type};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EnumEntry {
|
||||||
|
Const(str),
|
||||||
|
Tuple(str, TypeExpression[]),
|
||||||
|
Struct(str, TypeDeclaration[])
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_enum_entry(lexer: Lexer) -> EnumEntry {
|
||||||
|
entry_name: str = parse_identifier(lexer);
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Open))) do {
|
||||||
|
entry_types: TypeExpression[] = [parse_type(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) entry_types = entry_types + [parse_type(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
return EnumEntry.Tuple(entry_name, entry_types);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenCurly))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) return EnumEntry.Struct(entry_name, []);
|
||||||
|
|
||||||
|
assert false, "Unimplemented parse_enum_entry";
|
||||||
|
}
|
||||||
|
|
||||||
|
return EnumEntry.Const(entry_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Expression {
|
||||||
|
FunctionCall(Expression, Expression[]),
|
||||||
|
Variable(str),
|
||||||
|
ArrayAccess(Expression, Expression),
|
||||||
|
Array(Expression[]),
|
||||||
|
FieldAccess(Expression, str),
|
||||||
|
Number(int),
|
||||||
|
String(str),
|
||||||
|
Tuple(Expression[]),
|
||||||
|
StructInstantiation(Expression, (str, Expression)[]),
|
||||||
|
LoopComrehension(Expression, str, Expression),
|
||||||
|
Return(Expression),
|
||||||
|
Ternary(Expression, Expression, Expression),
|
||||||
|
Or(Expression, Expression),
|
||||||
|
And(Expression, Expression),
|
||||||
|
Bor(Expression, Expression),
|
||||||
|
Bxor(Expression, Expression),
|
||||||
|
Band(Expression, Expression),
|
||||||
|
Equal(Expression, Expression),
|
||||||
|
NotEqual(Expression, Expression),
|
||||||
|
LessThan(Expression, Expression),
|
||||||
|
GreaterThan(Expression, Expression),
|
||||||
|
LessThanOrEqual(Expression, Expression),
|
||||||
|
GreaterThanOrEqual(Expression, Expression),
|
||||||
|
ShiftLeft(Expression, Expression),
|
||||||
|
ShiftRight(Expression, Expression),
|
||||||
|
Addition(Expression, Expression),
|
||||||
|
Subtract(Expression, Expression),
|
||||||
|
Multiplication(Expression, Expression),
|
||||||
|
Division(Expression, Expression),
|
||||||
|
Modulo(Expression, Expression),
|
||||||
|
Bnot(Expression),
|
||||||
|
Not(Expression),
|
||||||
|
UnaryPlus(Expression),
|
||||||
|
UnaryMinus(Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalExpression {
|
||||||
|
Some(Expression),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_struct_argument(lexer: Lexer) -> (str, Expression) {
|
||||||
|
parameter: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Equal));
|
||||||
|
return (parameter, parse_expression(lexer));
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_primary(lexer: Lexer) -> Expression {
|
||||||
|
base_expression: Expression;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Open))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) base_expression = Expression.Tuple([]);
|
||||||
|
else {
|
||||||
|
elements: Expression[] = [parse_expression(lexer)];
|
||||||
|
singleton: bool = false;
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) do {
|
||||||
|
if lexer_check_token(lexer, TokenContents.Symbol(Symbol.Close)) do {
|
||||||
|
singleton = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elements = elements + [parse_expression(lexer)];
|
||||||
|
}
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
base_expression = singleton || len(elements) > 1? Expression.Tuple(elements): elements[0];
|
||||||
|
}
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenSquare))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseSquare))) base_expression = Expression.Array([]);
|
||||||
|
else {
|
||||||
|
expressions: Expression[] = [parse_expression(lexer)];
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.For))) do {
|
||||||
|
variable: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Keyword(Keyword.In));
|
||||||
|
expression: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_expression = Expression.LoopComrehension(expressions[0], variable, expression);
|
||||||
|
} else {
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) expressions = expressions + [parse_expression(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_expression = Expression.Array(expressions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match lexer_next_token(lexer).contents in {
|
||||||
|
case String(string) base_expression = Expression.String(string);
|
||||||
|
case Number(number) base_expression = Expression.Number(number);
|
||||||
|
case Identifier(string) base_expression = Expression.Variable(string);
|
||||||
|
case _token assert false, "Expected identifier, but got %s!" % token_to_str(_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while lexer_check_tokens(lexer, [TokenContents.Symbol(Symbol.Open), TokenContents.Symbol(Symbol.OpenSquare), TokenContents.Symbol(Symbol.Dot), TokenContents.Symbol(Symbol.OpenCurly)]) do {
|
||||||
|
match lexer_next_token(lexer).contents in {
|
||||||
|
case Symbol(symbol) match symbol in {
|
||||||
|
case Dot base_expression = Expression.FieldAccess(base_expression, parse_identifier(lexer));
|
||||||
|
case Open do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) base_expression = Expression.FunctionCall(base_expression, []);
|
||||||
|
else {
|
||||||
|
arguments: Expression[] = [parse_expression(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) arguments = arguments + [parse_expression(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
base_expression = Expression.FunctionCall(base_expression, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case OpenSquare do {
|
||||||
|
index: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_expression = Expression.ArrayAccess(base_expression, index);
|
||||||
|
}
|
||||||
|
case OpenCurly do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) base_expression = Expression.StructInstantiation(base_expression, []);
|
||||||
|
else {
|
||||||
|
struct_arguments: (str, Expression)[] = [parse_struct_argument(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) struct_arguments = struct_arguments + [parse_struct_argument(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
base_expression = Expression.StructInstantiation(base_expression, struct_arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented parse_primary symbol %s" % symbol_to_str(symbol);
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented parse_primary %s" % token_to_str(lexer_next_token(lexer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base_expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_unary(lexer: Lexer) -> Expression {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Tilde))) return Expression.Bnot(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Exclamation))) return Expression.Not(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Plus))) return Expression.UnaryPlus(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Dash))) return Expression.UnaryMinus(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Return))) return Expression.Return(parse_unary(lexer));
|
||||||
|
return parse_primary(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
precedences: (Symbol, (Expression, Expression) -> Expression)[][] = [
|
||||||
|
[(Symbol.Dpipe, Expression.Or)],
|
||||||
|
[(Symbol.Dampersand, Expression.And)],
|
||||||
|
[(Symbol.Pipe, Expression.Bor)],
|
||||||
|
[(Symbol.Carot, Expression.Bxor)],
|
||||||
|
[(Symbol.Ampersand, Expression.Band)],
|
||||||
|
[(Symbol.Dequal, Expression.Equal), (Symbol.NotEqual, Expression.NotEqual)],
|
||||||
|
[(Symbol.Left, Expression.LessThan), (Symbol.Right, Expression.GreaterThan), (Symbol.LesserEqual, Expression.LessThanOrEqual), (Symbol.GreaterEqual, Expression.GreaterThanOrEqual)],
|
||||||
|
[(Symbol.Dleft, Expression.ShiftLeft), (Symbol.Dright, Expression.ShiftRight)],
|
||||||
|
[(Symbol.Plus, Expression.Addition), (Symbol.Dash, Expression.Subtract)],
|
||||||
|
[(Symbol.Asterisk, Expression.Multiplication), (Symbol.Slash, Expression.Division), (Symbol.Percent, Expression.Modulo)]
|
||||||
|
];
|
||||||
|
|
||||||
|
func parse_expression_at_level(lexer: Lexer, level: int) -> Expression {
|
||||||
|
if level >= len(precedences) return parse_unary(lexer);
|
||||||
|
left: Expression = parse_expression_at_level(lexer, level+1);
|
||||||
|
tokens: TokenContents[] = [TokenContents.Symbol(symbol_expressor[0]) for symbol_expressor in precedences[level]];
|
||||||
|
expressor: (Expression, Expression) -> Expression;
|
||||||
|
while lexer_check_tokens(lexer, tokens) do {
|
||||||
|
match lexer_next_token(lexer).contents in {
|
||||||
|
case Symbol(symbol) do {
|
||||||
|
for symbol_expressor in precedences[level] if symbol_expressor[0] == symbol expressor = symbol_expressor[1];
|
||||||
|
left = expressor(left, parse_expression_at_level(lexer, level+1));
|
||||||
|
}
|
||||||
|
case _ assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_ternary(lexer: Lexer) -> Expression {
|
||||||
|
expression: Expression = parse_expression_at_level(lexer, 0);
|
||||||
|
if !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.QuestionMark))) return expression;
|
||||||
|
if_true: Expression = parse_expression_at_level(lexer, 0);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Colon));
|
||||||
|
if_false: Expression = parse_ternary(lexer);
|
||||||
|
return Expression.Ternary(expression, if_true, if_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_expression(lexer: Lexer) -> Expression {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Return))) return Expression.Return(parse_expression(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Lambda))) do {
|
||||||
|
parameters: TypeDeclaration[];
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.EqualArrow))) parameters = [];
|
||||||
|
else do {
|
||||||
|
parameters = [parse_type_declaration(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) parameters = parameters + [parse_type_declaration(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.EqualArrow));
|
||||||
|
}
|
||||||
|
return Expression.Lambda(parameters, parse_expression(lexer));
|
||||||
|
}
|
||||||
|
return parse_ternary(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Statement {
|
||||||
|
Statements(Statement[]),
|
||||||
|
EnumDefinition(str, EnumEntry[]),
|
||||||
|
StructDefinition(str, TypeDeclaration[]),
|
||||||
|
FunctionDefinition(str, TypeDeclaration[], OptionalTypeExpression, Statement),
|
||||||
|
Expression(Expression),
|
||||||
|
Assignment(Expression, Expression, OptionalTypeExpression),
|
||||||
|
TypeDeclaration(TypeDeclaration),
|
||||||
|
If(Expression, Statement, OptionalStatement),
|
||||||
|
While(Expression, Statement),
|
||||||
|
DoWhile(Statement, OptionalExpression),
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Match(Expression, (Expression, Statement)[]),
|
||||||
|
Assert(Expression, OptionalExpression),
|
||||||
|
ForLoop(str, Expression, Statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
func statement_to_str(statement: Statement) -> str {
|
||||||
|
match statement in {
|
||||||
|
case EnumDefinition(name, entries) return "Enum %s" % name;
|
||||||
|
}
|
||||||
|
assert false, "Unimplemented statement_to_str";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalStatement {
|
||||||
|
Some(Statement),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_identifier(lexer: Lexer) -> str {
|
||||||
|
identifier_token: Token = lexer_next_token(lexer);
|
||||||
|
match identifier_token.contents in {
|
||||||
|
case Identifier(identifier) return identifier;
|
||||||
|
case _ assert false, "Expected identifier, but got %s!" % token_to_str(identifier_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_number(lexer: Lexer) -> int {
|
||||||
|
number_token: Token = lexer_next_token(lexer);
|
||||||
|
match number_token.contents in {
|
||||||
|
case Number(number) return number;
|
||||||
|
case _ assert false, "Expected number!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_string(lexer: Lexer) -> str {
|
||||||
|
string_token: Token = lexer_next_token(lexer);
|
||||||
|
match string_token.contents in {
|
||||||
|
case String(string) return string;
|
||||||
|
case _ assert false, "Expected string!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_valid_target(expression: Expression) -> bool {
|
||||||
|
match expression in {
|
||||||
|
case FieldAccess(subexpression, _) return is_valid_target(subexpression);
|
||||||
|
case Variable(_) return true;
|
||||||
|
case _ assert false, "Unimplemented is_valid_target %s" % expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_statement(lexer: Lexer) -> Statement {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Enum))) do {
|
||||||
|
enum_name: str = parse_identifier(lexer);
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.OpenCurly));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) return Statement.EnumDefinition(enum_name, []);
|
||||||
|
|
||||||
|
enum_entries: EnumEntry[] = [parse_enum_entry(lexer)];
|
||||||
|
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) enum_entries = enum_entries + [parse_enum_entry(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
return Statement.EnumDefinition(enum_name, enum_entries);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Struct))) do {
|
||||||
|
struct_name: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.OpenCurly));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) return Statement.StructDefinition(struct_name, []);
|
||||||
|
|
||||||
|
struct_entries: TypeDeclaration[] = [parse_type_declaration(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) struct_entries = struct_entries + [parse_type_declaration(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
return Statement.StructDefinition(struct_name, struct_entries);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Func))) do {
|
||||||
|
function_name: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Open));
|
||||||
|
function_arguments: TypeDeclaration[] = [];
|
||||||
|
if !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) do {
|
||||||
|
function_arguments = function_arguments + [parse_type_declaration(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) function_arguments = function_arguments + [parse_type_declaration(lexer)];
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
function_return_type: OptionalTypeExpression = OptionalTypeExpression.None;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Arrow))) function_return_type = OptionalTypeExpression.Some(parse_type(lexer));
|
||||||
|
|
||||||
|
function_body: Statement = parse_statement(lexer);
|
||||||
|
return Statement.FunctionDefinition(function_name, function_arguments, function_return_type, function_body);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.If))) do {
|
||||||
|
return Statement.If(parse_expression(lexer), parse_statement(lexer), is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Else)))? OptionalStatement.Some(parse_statement(lexer)): OptionalStatement.None);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Match))) do {
|
||||||
|
value: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Keyword(Keyword.In));
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.OpenCurly));
|
||||||
|
|
||||||
|
cases: (Expression, Statement)[] = [];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Case))) cases = cases + [(parse_expression(lexer), parse_statement(lexer))];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
return Statement.Match(value, cases);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Assert))) do {
|
||||||
|
condition: Expression = parse_expression(lexer);
|
||||||
|
message: OptionalExpression = is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma)))? OptionalExpression.Some(parse_expression(lexer)): OptionalExpression.None;
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Assert(condition, message);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Do))) do {
|
||||||
|
body: Statement = parse_statement(lexer);
|
||||||
|
condition: OptionalExpression = OptionalExpression.None;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.While))) do {
|
||||||
|
condition = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
}
|
||||||
|
return Statement.DoWhile(body, condition);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.While))) do {
|
||||||
|
return Statement.While(parse_expression(lexer), parse_statement(lexer));
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.For))) do {
|
||||||
|
variable: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Keyword(Keyword.In));
|
||||||
|
expression: Expression = parse_expression(lexer);
|
||||||
|
body: Statement = parse_statement(lexer);
|
||||||
|
return Statement.ForLoop(variable, expression, body);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Continue))) do {
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Continue;
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Break))) do {
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Break;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenCurly))) do {
|
||||||
|
statements = [];
|
||||||
|
while !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) statements = statements + [parse_statement(lexer)];
|
||||||
|
return Statement.Statements(statements);
|
||||||
|
} else {
|
||||||
|
expression: Expression = parse_expression(lexer);
|
||||||
|
type_: OptionalTypeExpression = OptionalTypeExpression.None;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Colon))) do {
|
||||||
|
match expression in {
|
||||||
|
case Variable(_) type_ = OptionalTypeExpression.Some(parse_type(lexer));
|
||||||
|
case _ assert false, "Invalid target";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Equal))) do {
|
||||||
|
assert is_valid_target(expression), "Invalid target!";
|
||||||
|
right_expression: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Assignment(expression, right_expression, type_);
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
match expression in {
|
||||||
|
case Variable(name) match type_ in {
|
||||||
|
case Some(type_expression) return Statement.TypeDeclaration(TypeDeclaration{name=name, type_=type_expression});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Statement.Expression(expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Parsing...\n");
|
||||||
|
lexer: Lexer = lexer_from_file("test.pyc");
|
||||||
|
statements: Statement[] = [];
|
||||||
|
while !is_some_token(lexer_take_token(lexer, TokenContents.Eof)) statements = statements + [parse_statement(lexer)];
|
3
ppp.ppp
Normal file
3
ppp.ppp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import "ppp_interpreter.ppp";
|
||||||
|
|
||||||
|
interpret_file("ppp.ppp", {});
|
7
ppp.py
Normal file
7
ppp.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from ppp_interpreter import interpret_file
|
||||||
|
|
||||||
|
import sys
|
||||||
|
try:
|
||||||
|
interpret_file(sys.argv[1], {})
|
||||||
|
except RecursionError as e:
|
||||||
|
print("Recursion")
|
95
ppp_ast.ppp
Normal file
95
ppp_ast.ppp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
enum TypeExpression {
|
||||||
|
Tuple(TypeExpression[]),
|
||||||
|
Union(TypeExpression[]),
|
||||||
|
List(TypeExpression),
|
||||||
|
Array(TypeExpression, int),
|
||||||
|
Name(str),
|
||||||
|
Specification(TypeExpression, TypeExpression[]),
|
||||||
|
Function(TypeExpression[], TypeExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalTypeExpression {
|
||||||
|
Some(TypeExpression),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TypeDeclaration {
|
||||||
|
name: str,
|
||||||
|
type_: TypeExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EnumEntry {
|
||||||
|
name: str,
|
||||||
|
types: TypeExpression[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum Expression {
|
||||||
|
FunctionCall(Expression, Expression[]),
|
||||||
|
Variable(str),
|
||||||
|
ArrayAccess(Expression, Expression),
|
||||||
|
Array(Expression[]),
|
||||||
|
FieldAccess(Expression, str),
|
||||||
|
Number(int),
|
||||||
|
String(str),
|
||||||
|
Tuple(Expression[]),
|
||||||
|
StructInstantiation(Expression, (str, Expression)[]),
|
||||||
|
LoopComrehension(Expression, str, Expression),
|
||||||
|
Dictionary((Expression, Expression)[]),
|
||||||
|
DictComprehension((Expression, Expression), str, Expression),
|
||||||
|
Return(Expression),
|
||||||
|
Ternary(Expression, Expression, Expression),
|
||||||
|
Or(Expression, Expression),
|
||||||
|
And(Expression, Expression),
|
||||||
|
Bor(Expression, Expression),
|
||||||
|
Bxor(Expression, Expression),
|
||||||
|
Band(Expression, Expression),
|
||||||
|
Equal(Expression, Expression),
|
||||||
|
NotEqual(Expression, Expression),
|
||||||
|
LessThan(Expression, Expression),
|
||||||
|
GreaterThan(Expression, Expression),
|
||||||
|
LessThanOrEqual(Expression, Expression),
|
||||||
|
GreaterThanOrEqual(Expression, Expression),
|
||||||
|
ShiftLeft(Expression, Expression),
|
||||||
|
ShiftRight(Expression, Expression),
|
||||||
|
Addition(Expression, Expression),
|
||||||
|
Subtract(Expression, Expression),
|
||||||
|
Multiplication(Expression, Expression),
|
||||||
|
Division(Expression, Expression),
|
||||||
|
Modulo(Expression, Expression),
|
||||||
|
Bnot(Expression),
|
||||||
|
Not(Expression),
|
||||||
|
UnaryPlus(Expression),
|
||||||
|
UnaryMinus(Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalExpression {
|
||||||
|
Some(Expression),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum Statement {
|
||||||
|
Statements(Statement[]),
|
||||||
|
EnumDefinition(str, EnumEntry[]),
|
||||||
|
StructDefinition(str, TypeDeclaration[]),
|
||||||
|
FunctionDefinition(str, TypeDeclaration[], OptionalTypeExpression, Statement),
|
||||||
|
Expression(Expression),
|
||||||
|
Assignment(Expression, Expression, OptionalTypeExpression),
|
||||||
|
TypeDeclaration(TypeDeclaration),
|
||||||
|
If(Expression, Statement, OptionalStatement),
|
||||||
|
While(Expression, Statement),
|
||||||
|
DoWhile(Statement, OptionalExpression),
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Match(Expression, (Expression, Statement)[]),
|
||||||
|
Assert(Expression, OptionalExpression),
|
||||||
|
ForLoop(str, Expression, Statement),
|
||||||
|
Import(Expression),
|
||||||
|
TypeDefinition(str, TypeExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalStatement {
|
||||||
|
Some(Statement),
|
||||||
|
None
|
||||||
|
}
|
545
ppp_ast.py
Normal file
545
ppp_ast.py
Normal file
@ -0,0 +1,545 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
### Types ###
|
||||||
|
|
||||||
|
class TypeExpression(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def represent(self) -> str: ...
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TupleTypeExpr(TypeExpression):
|
||||||
|
types: List[TypeExpression]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class UnionTypeExpr(TypeExpression):
|
||||||
|
types: List[TypeExpression]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ListTypeExpr(TypeExpression):
|
||||||
|
type: TypeExpression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ArrayTypeExpr(TypeExpression):
|
||||||
|
type: TypeExpression
|
||||||
|
number: int
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TypeName(TypeExpression):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TypeSpecification(TypeExpression):
|
||||||
|
type: TypeExpression
|
||||||
|
types: List[TypeExpression]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionTypeExpr(TypeExpression):
|
||||||
|
arguments: List[TypeExpression]
|
||||||
|
return_type: TypeExpression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
### Statements ###
|
||||||
|
|
||||||
|
class Statement:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Statements(Statement):
|
||||||
|
statements: List[Statement]
|
||||||
|
|
||||||
|
### Enums + Struct ###
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EnumEntry:
|
||||||
|
name: str
|
||||||
|
types: List[TypeExpression]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EnumDefinition(Statement):
|
||||||
|
name: str
|
||||||
|
entries: List[EnumEntry]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TypeDeclaration:
|
||||||
|
name: str
|
||||||
|
type: TypeExpression
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StructDefinition(Statement):
|
||||||
|
name: str
|
||||||
|
entries: List[TypeDeclaration]
|
||||||
|
|
||||||
|
### Function ###
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionDefinition(Statement):
|
||||||
|
name: str
|
||||||
|
arguments: list[TypeDeclaration]
|
||||||
|
return_type: Optional[TypeExpression]
|
||||||
|
body: Statement
|
||||||
|
|
||||||
|
### Expressions ###
|
||||||
|
|
||||||
|
class Expression(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def precedence(self) -> int: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def represent(self) -> str: ...
|
||||||
|
|
||||||
|
def wrap(self, other: 'Expression') -> str:
|
||||||
|
if self.precedence() > other.precedence(): return '('+other.represent()+')'
|
||||||
|
return other.represent()
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionCall(Expression):
|
||||||
|
function: Expression
|
||||||
|
arguments: List[Expression]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.function)+"("+', '.join([argument.represent() for argument in self.arguments])+")"
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Variable(Expression):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ArrayAccess(Expression):
|
||||||
|
array: Expression
|
||||||
|
index: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.array)+"["+self.index.represent()+"]"
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Array(Expression):
|
||||||
|
array: List[Expression]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return "["+', '.join(map(str, self.array))+"]"
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FieldAccess(Expression):
|
||||||
|
expression: Expression
|
||||||
|
field: str
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.expression)+"."+self.field
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Number(Expression):
|
||||||
|
number: int
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return str(self.number)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class String(Expression):
|
||||||
|
string: str
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return repr(self.string)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TupleExpr(Expression):
|
||||||
|
elements: List[Expression]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return f"([{', '.join([element.represent() for element in self.elements])}])"
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StructInstantiation(Expression):
|
||||||
|
struct: Expression
|
||||||
|
arguments: List[Tuple[str, Expression]]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LoopComprehension(Expression):
|
||||||
|
body: Expression
|
||||||
|
variable: str # TODO: Pattern matching
|
||||||
|
array: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DictionaryExpr(Expression):
|
||||||
|
dict: List[Tuple[Expression, Expression]]
|
||||||
|
|
||||||
|
def represent(self) -> str: assert False
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DictComprehension(Expression):
|
||||||
|
body: Tuple[Expression, Expression]
|
||||||
|
variable: str # TODO: Pattern matching
|
||||||
|
array: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 13
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Return(Expression):
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
# TODO: This will have to be improved
|
||||||
|
return "return "+self.wrap(self.expression)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 0
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Lambda(Expression):
|
||||||
|
parameters: List[TypeDeclaration]
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 0
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Ternary(Expression):
|
||||||
|
condition: Expression
|
||||||
|
if_true: Expression
|
||||||
|
if_false: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.if_true)+" if "+self.wrap(self.condition)+" else "+self.wrap(self.if_false)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 1
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Or(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" or "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 2
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class And(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" and "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 3
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Bor(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" | "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 4
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Bxor(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" ^ "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 5
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Band(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" & "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 6
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Equal(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" == "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 7
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NotEqual(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" != "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 7
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LessThan(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" < "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 8
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GreaterThan(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" > "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 8
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LessThanOrEqual(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" <= "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 8
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GreaterThanOrEqual(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" >= "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 8
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ShiftLeft(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" << "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 9
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ShiftRight(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" >> "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 9
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Addition(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" + "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 10
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Subtract(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" - "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 10
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Multiplication(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" * "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 11
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Division(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" / "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 11
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Modulo(Expression):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return self.wrap(self.lhs)+" % "+self.wrap(self.rhs)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 11
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Bnot(Expression):
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return "~"+self.wrap(self.expression)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 12
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Not(Expression):
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return "!"+self.wrap(self.expression)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 12
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class UnaryPlus(Expression):
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return "+"+self.wrap(self.expression)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 12
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class UnaryMinus(Expression):
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
return "-"+self.wrap(self.expression)
|
||||||
|
|
||||||
|
def precedence(self) -> int: return 12
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ExpressionStatement(Statement):
|
||||||
|
expression: Expression
|
||||||
|
|
||||||
|
### Assignment + Declaration ###
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Assignment(Statement):
|
||||||
|
lhs: Expression
|
||||||
|
rhs: Expression
|
||||||
|
type: Optional[TypeExpression] = None
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TypeDeclarationStatement(Statement):
|
||||||
|
type_declaration: TypeDeclaration
|
||||||
|
|
||||||
|
### Control flow ###
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IfStatement(Statement):
|
||||||
|
condition: Expression
|
||||||
|
body: Statement
|
||||||
|
else_body: Optional[Statement]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WhileStatement(Statement):
|
||||||
|
condition: Expression
|
||||||
|
body: Statement
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DoWhileStatement(Statement):
|
||||||
|
body: Statement
|
||||||
|
condition: Optional[Expression]
|
||||||
|
|
||||||
|
# TODO: Maybe do something similar to return with these two?
|
||||||
|
@dataclass
|
||||||
|
class BreakStatement(Statement):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ContinueStatement(Statement):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MatchStatement(Statement):
|
||||||
|
value: Expression
|
||||||
|
cases: List[Tuple[Expression, Statement]]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class AssertStatement(Statement):
|
||||||
|
condition: Expression
|
||||||
|
message: Optional[Expression]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ForLoop(Statement):
|
||||||
|
variable: str # TODO allow for pattern matching
|
||||||
|
array: Expression
|
||||||
|
body: Statement
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Import(Statement):
|
||||||
|
file: Expression
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TypeDefinition(Statement):
|
||||||
|
name: str
|
||||||
|
expression: TypeExpression
|
288
ppp_interpreter.ppp
Normal file
288
ppp_interpreter.ppp
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
import "ppp_tokens.ppp";
|
||||||
|
import "ppp_lexer.ppp";
|
||||||
|
import "ppp_ast.ppp";
|
||||||
|
import "ppp_parser.ppp";
|
||||||
|
import "ppp_types.ppp";
|
||||||
|
import "ppp_object.ppp";
|
||||||
|
import "ppp_stdlib.ppp";
|
||||||
|
|
||||||
|
enum VariableState {
|
||||||
|
Declared(Type, Object),
|
||||||
|
Undeclared(Type),
|
||||||
|
Constant(Type, Object)
|
||||||
|
}
|
||||||
|
|
||||||
|
func declared_from_obj(obj: Object) -> VariableState return VariableState.Declared(object_get_type(obj), obj);
|
||||||
|
|
||||||
|
type Module = dict[str, VariableState];
|
||||||
|
|
||||||
|
struct Program {
|
||||||
|
modules: dict[str, Module],
|
||||||
|
contexts: dict[str, VariableState][]
|
||||||
|
}
|
||||||
|
|
||||||
|
func program_exists(program: Program, name: str) -> bool {
|
||||||
|
for context in program.contexts do {
|
||||||
|
for name_ in context do {
|
||||||
|
if name_ == name return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func program_access_variable(program: Program, name: str) -> Object {
|
||||||
|
i: int = len(program.contexts) - 1;
|
||||||
|
while i >= 0 do {
|
||||||
|
context: dict[str, VariableState] = program.contexts[i];
|
||||||
|
for name_ in context do {
|
||||||
|
if name_ == name do {
|
||||||
|
value: VariableState = context[name];
|
||||||
|
match value in {
|
||||||
|
case Declared(_, value) return value;
|
||||||
|
case Const(_, value) return value;
|
||||||
|
case Undeclared(_) assert false, "'%s' is not defined!" % name;
|
||||||
|
case _ assert false, "Unimplemented program_access_variable %s" % value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i - 1;
|
||||||
|
}
|
||||||
|
assert false, "'%s' is not defined!" % name;
|
||||||
|
}
|
||||||
|
|
||||||
|
func program_declare_variable(program: Program, name: str, type_: Type) {
|
||||||
|
for name_ in program.contexts[len(program.contexts)-1] assert name_ != name, "'%s' has already been declared!" % name;
|
||||||
|
program.contexts[len(program.contexts)-1][name] = VariableState.Undeclared(type_);
|
||||||
|
}
|
||||||
|
|
||||||
|
func program_assign_variable(program: Program, name: str, value: Object) {
|
||||||
|
i: int = len(program.contexts) - 1;
|
||||||
|
while i >= 0 do {
|
||||||
|
context: dict[str, VariableState] = program.contexts[i];
|
||||||
|
for name_ in context do {
|
||||||
|
if name_ == name do {
|
||||||
|
variable_type: Type;
|
||||||
|
match context[name] in {
|
||||||
|
case Undeclared(type_) variable_type = type_;
|
||||||
|
case _ assert false, "Unimplemented program_assign_variable %s" % context[name];
|
||||||
|
}
|
||||||
|
assert type_is_subtype_of(object_get_type(value), variable_type), "In the assignment of '%s', expected value of type '%s', but got a value of type '%s'!" % (name, type_represent(variable_type, type_represent(object_get_type(value))));
|
||||||
|
context[name] = VariableState.Declared(variable_type, value);
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i - 1;
|
||||||
|
}
|
||||||
|
assert false, "'%s' doesn't exist!" % name;
|
||||||
|
}
|
||||||
|
|
||||||
|
func program_declare_and_assign_variable(program: Program, name: str, value: Object) {
|
||||||
|
program_declare_variable(program, name, object_get_type(value));
|
||||||
|
program_assign_variable(program, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculate_expression(expression: Expression, program: Program) -> Object {
|
||||||
|
match expression in {
|
||||||
|
case String(string) return Object.Str(string);
|
||||||
|
case Array(array_) do {
|
||||||
|
if len(array_) == 0 return Object.List(Type.List(Type.Variable("")), []);
|
||||||
|
elements_type: Type;
|
||||||
|
array_elements: Object[] = [];
|
||||||
|
for i in range(0, len(array_)) do {
|
||||||
|
element: Object = calculate_expression(array_[i], program);
|
||||||
|
if i == 0 elements_type = object_get_type(element);
|
||||||
|
else assert type_is_subtype_of(object_get_type(element), elements_type), "Array element invalid type";
|
||||||
|
array_elements = array_elements + [element];
|
||||||
|
}
|
||||||
|
assert false, "array %s" % array_elements;
|
||||||
|
}
|
||||||
|
case Dictionary(dict_) do {
|
||||||
|
if len(dict_) == 0 return Object.Dictionary(Type.Dictionary(Type.Variable(""), Type.Variable("")), {});
|
||||||
|
key_type: Type;
|
||||||
|
value_type: Type;
|
||||||
|
dict: dict[Object, Object] = {};
|
||||||
|
for i in range(0, len(dict_)) do {
|
||||||
|
key: Object = calculate_expression(dict_[i][0], program);
|
||||||
|
value: Object = calculate_expression(dict_[i][1], program);
|
||||||
|
if i == 0 do {
|
||||||
|
key_type = object_get_type(key);
|
||||||
|
value_type = object_get_type(value);
|
||||||
|
} else {
|
||||||
|
assert type_is_subtype_of(object_get_type(key), key_type), "Dict element invalid key type";
|
||||||
|
assert type_is_subtype_of(object_get_type(value), value_type), "Dict element invalid value type";
|
||||||
|
}
|
||||||
|
dict[key] = value;
|
||||||
|
}
|
||||||
|
assert false, "dict %s" % dict;
|
||||||
|
}
|
||||||
|
case FieldAccess(expression_, field) do {
|
||||||
|
value: Object = calculate_expression(expression_, program);
|
||||||
|
match value in {
|
||||||
|
case Type(type_) match type_ in {
|
||||||
|
case Enum(name, members, _) do {
|
||||||
|
for member in members do {
|
||||||
|
if member == field do {
|
||||||
|
if len(members[field]) == 0 return Object.EnumValue(type_, field, []);
|
||||||
|
func return_member(name: str, parameters: (str, Type)[], return_type: Type, statement: Statement, args: Object[]) -> Object {
|
||||||
|
match return_type in {
|
||||||
|
case Enum(_, _, _) do {}
|
||||||
|
case _ assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(args) == len(parameters), "%s.%s expected %s arguments but got %s!" % (type_represent(type_), field, int_to_str(len(parameters)), int_to_str(len(args)));
|
||||||
|
for i in range(0, len(args)) assert type_is_subtype_of(object_get_type(args[i]), parameters[i][1]);
|
||||||
|
return Object.EnumValue(return_type, name, args);
|
||||||
|
}
|
||||||
|
return Object.Function(Type.Function(members[field], type_), (field, [("", member_type) for member_type in members[field]], type_, Statement.Statements([]), return_member));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert false, "g";
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented calculate_expression field access type %s" % type_represent(type_);
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented calculate_expression field access %s" % value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Variable(name) return program_access_variable(program, name);
|
||||||
|
case _ assert false, "Unimplemented calculate_expression %s" % expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculate_type_expression(expression: TypeExpression, program: Program, must_resolve: bool) -> Type {
|
||||||
|
match expression in {
|
||||||
|
case Name(name) do {
|
||||||
|
if !program_exists(program, name) && !must_resolve return Type.Variable(name);
|
||||||
|
type_obj: Object = program_access_variable(program, name);
|
||||||
|
match type_obj in {
|
||||||
|
case Type(type_) return type_;
|
||||||
|
case _ assert false, "Unimplemented %s" % type_obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case List(type_) return Type.List(calculate_type_expression(type_, program, must_resolve));
|
||||||
|
case Tuple(types) return Type.Tuple([calculate_type_expression(type_, program, must_resolve) for type_ in types]);
|
||||||
|
case _ assert false, "Unimplemented calculate_type_expression %s" % expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update_types(type_: Type, program: Program) {
|
||||||
|
type_name: str;
|
||||||
|
match type_ in {
|
||||||
|
case Enum(name, _, _) type_name = name;
|
||||||
|
case Struct(name, _, _) type_name = name;
|
||||||
|
|
||||||
|
case _ assert false, "Unimplemented update_types %s" % type_represent(type_);
|
||||||
|
}
|
||||||
|
for context in program.contexts do {
|
||||||
|
for variable_ in context do {
|
||||||
|
match context[variable_] in {
|
||||||
|
case Declared(variable_type, value) match value in {
|
||||||
|
case Type(variable_type_) do {
|
||||||
|
assert variable_type == Type.Type;
|
||||||
|
type_fill(variable_type_, {type_name: type_}, []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Undeclared(_) do {}
|
||||||
|
case _ assert false, "Unimplemented update_types %s" % context[variable_];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Result {
|
||||||
|
Return(Object),
|
||||||
|
Continue,
|
||||||
|
Break,
|
||||||
|
Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
func interpret_statements(statements: Statement[], program: Program) -> Result {
|
||||||
|
for statement in statements do {
|
||||||
|
match statement in {
|
||||||
|
case Import(file_) do {
|
||||||
|
file_path: Object = calculate_expression(file_, program);
|
||||||
|
match file_path in {
|
||||||
|
case Str(file_path_str) do {
|
||||||
|
found: bool = false;
|
||||||
|
module: Module;
|
||||||
|
for module_ in program.modules do {
|
||||||
|
if module_ == file_path_str do {
|
||||||
|
found = true;
|
||||||
|
module = program.modules[module_];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found do {
|
||||||
|
print("Importing %s\n" % file_path_str);
|
||||||
|
module = interpret_file(file_path_str, program.modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
for variable in module do program.contexts[0][variable] = module[variable];
|
||||||
|
|
||||||
|
if !found program.modules[file_path_str] = module;
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented interpret_statement import %s" % file_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EnumDefinition(name, entries) do {
|
||||||
|
enum_type: Type = Type.Enum(name, {entry.name: [calculate_type_expression(type_, program, false) for type_ in entry.types] for entry in entries}, []);
|
||||||
|
program_declare_and_assign_variable(program, name, Object.Type(enum_type));
|
||||||
|
update_types(enum_type, program);
|
||||||
|
}
|
||||||
|
case FunctionDefinition(name, arguments_, return_type_, body) do {
|
||||||
|
func run_function(name: str, arguments: (str, Type)[], return_type: Type, body: Statement, args: Object[]) -> Object {
|
||||||
|
assert false, "run_function";
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments: (str, Type)[] = [(argument.name, calculate_type_expression(argument.type_, program, true)) for argument in arguments_];
|
||||||
|
return_type: Type;
|
||||||
|
match return_type_ in {
|
||||||
|
case Some(type_) return_type = calculate_type_expression(type_, program, true);
|
||||||
|
case None return_type = Type.Void;
|
||||||
|
}
|
||||||
|
function_type: Type = Type.Function([argument[1] for argument in arguments], return_type);
|
||||||
|
object: Object = Object.Function(function_type, (name, arguments, return_type, body, run_function));
|
||||||
|
program_declare_and_assign_variable(program, name, object);
|
||||||
|
}
|
||||||
|
case StructDefinition(name, entries) do {
|
||||||
|
struct_type: Type = Type.Struct(name, {entry.name: calculate_type_expression(entry.type_, program, false) for entry in entries}, []);
|
||||||
|
program_declare_and_assign_variable(program, name, Object.Type(struct_type));
|
||||||
|
update_types(struct_type, program);
|
||||||
|
}
|
||||||
|
case Assignment(lhs, rhs, type_) do {
|
||||||
|
assert is_valid_target(lhs);
|
||||||
|
match lhs in {
|
||||||
|
case Variable(name) do {
|
||||||
|
value: Object = calculate_expression(rhs, program);
|
||||||
|
match type_ in {
|
||||||
|
case _ assert false, "uuuu %s" % type_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented interpret_statement assignment %s" % lhs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented interpret_statement %s" % statement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result.Nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func interpret_file(file_path: str, modules: dict[str, Module]) -> Module {
|
||||||
|
print("Parsing %s...\n" % file_path);
|
||||||
|
lexer: Lexer = lexer_from_file(file_path);
|
||||||
|
statements: Statement[] = [];
|
||||||
|
while !is_some_token(lexer_take_token(lexer, TokenContents.Eof)) statements = statements + [parse_statement(lexer)];
|
||||||
|
new_variables: dict[str, VariableState] = {};
|
||||||
|
for variable in variables new_variables[variable] = declared_from_obj(variables[variable]);
|
||||||
|
program: Program = Program{modules=modules, contexts=[new_variables, {}]};
|
||||||
|
print("Interpreting %s...\n" % file_path);
|
||||||
|
return_value: Result = interpret_statements(statements, program);
|
||||||
|
assert len(program.contexts) == 2;
|
||||||
|
match return_value in {
|
||||||
|
case Nothing do {}
|
||||||
|
case Return(_) assert false, "Cannot return from outside a function!";
|
||||||
|
case Continue assert false, "Cannot continue from outside a loop!";
|
||||||
|
case Break assert false, "Cannot break from outside a loop!";
|
||||||
|
case _ assert false, "Unimplemented interpret_file return_value";
|
||||||
|
}
|
||||||
|
return program.contexts[1];
|
||||||
|
}
|
679
ppp_interpreter.py
Normal file
679
ppp_interpreter.py
Normal file
@ -0,0 +1,679 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Dict, List as List_, Optional, Tuple, Union
|
||||||
|
|
||||||
|
from ppp_ast import *
|
||||||
|
from ppp_lexer import Lexer
|
||||||
|
from ppp_object import Bool, Dictionary, EnumValue, Function, Hashable, Int, Object, Str, Struct, Tuple as TupleObject, List as ListObject, Return as ReturnObject, TypeObject, Dictionary as DictionaryObject, Void
|
||||||
|
from ppp_parser import is_valid_target, parse_statement
|
||||||
|
from ppp_tokens import EofToken
|
||||||
|
from ppp_stdlib import variables
|
||||||
|
from ppp_types import DictionaryType, EnumType, FunctionType, GenericType, Int as IntType, ListType, ReturnType, Str as StrType, StructType, TupleType, Type, TypeType, UnionType, VariableType, Void as VoidType
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Declared:
|
||||||
|
type: Type
|
||||||
|
value: Object
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_obj(obj: Object) -> 'Declared':
|
||||||
|
return Declared(obj.get_type(), obj)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Undeclared:
|
||||||
|
type: Type
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Constant:
|
||||||
|
type: Type
|
||||||
|
value: Object
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_obj(obj: Object) -> 'Declared':
|
||||||
|
return Declared(obj.get_type(), obj)
|
||||||
|
|
||||||
|
VariableState = Union[Declared, Undeclared, Constant]
|
||||||
|
|
||||||
|
Module = Dict[str, VariableState]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProgramState:
|
||||||
|
modules: Dict[str, Module] # TODO: What is the type of module?
|
||||||
|
contexts: List_[Dict[str, VariableState]]
|
||||||
|
|
||||||
|
def push_context(self, variables: Dict[str, VariableState]): self.contexts.append(variables)
|
||||||
|
def pop_context(self): self.contexts.pop()
|
||||||
|
|
||||||
|
def declare_variable(self, name: str, type: Type):
|
||||||
|
assert not (name in self.contexts[-1]), f"'{name}' has already been declared!"
|
||||||
|
self.contexts[-1][name] = Undeclared(type)
|
||||||
|
|
||||||
|
def assign_variable(self, name: str, value: Object):
|
||||||
|
for context in self.contexts[::-1]:
|
||||||
|
if name in context:
|
||||||
|
assert value.get_type().is_subtype_of(context[name].type), f"In the assignment of '{name}', expected value of type {context[name].type.represent()}, but got a value of type {value.get_type().represent()}!"
|
||||||
|
context[name] = Declared(context[name].type, value)
|
||||||
|
return
|
||||||
|
assert False, f"'{name}' doesn't exist!"
|
||||||
|
|
||||||
|
def declare_and_assign_variable(self, name: str, value: Object):
|
||||||
|
self.declare_variable(name, value.get_type())
|
||||||
|
self.assign_variable(name, value)
|
||||||
|
|
||||||
|
def exists(self, name: str) -> bool:
|
||||||
|
for context in self.contexts[::-1]:
|
||||||
|
if name in context: return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def access_variable(self, name: str) -> Object:
|
||||||
|
for context in self.contexts[::-1]:
|
||||||
|
if name in context:
|
||||||
|
value = context[name]
|
||||||
|
assert not isinstance(value, Undeclared), f"{name} is not declared!"
|
||||||
|
return value.value
|
||||||
|
assert False, f"'{name}' is not defined!"
|
||||||
|
|
||||||
|
def is_truthy(object: Object) -> bool:
|
||||||
|
match object:
|
||||||
|
case Bool(value): return value
|
||||||
|
case _: assert False, ("Unimplemented", object)
|
||||||
|
|
||||||
|
def calculate_expression(expression: Expression, program: ProgramState) -> Object:
|
||||||
|
match expression:
|
||||||
|
case FunctionCall(function_, arguments_):
|
||||||
|
function = calculate_expression(function_, program)
|
||||||
|
assert isinstance(function, Function), (function_, function)
|
||||||
|
name, parameters, return_type, body, func = function.function
|
||||||
|
|
||||||
|
arguments = [calculate_expression(argument, program) for argument in arguments_]
|
||||||
|
assert len(arguments) == len(parameters), f"{name} expected {len(parameters)} arguments, but got {len(arguments)}!"
|
||||||
|
|
||||||
|
for (argument, (parameter_name, parameter)) in zip(arguments, parameters):
|
||||||
|
assert argument.get_type().is_subtype_of(parameter), f"For argument '{parameter_name}' of '{name}', expected value of type {parameter.represent()}, but got {argument.get_type().represent()}!"
|
||||||
|
return_value = func(name, parameters, return_type, body, *arguments)
|
||||||
|
assert isinstance(return_value, Object), return_value
|
||||||
|
assert return_value.get_type().is_subtype_of(return_type)
|
||||||
|
return return_value
|
||||||
|
case Variable(name):
|
||||||
|
return program.access_variable(name)
|
||||||
|
case String(string):
|
||||||
|
return Str(string)
|
||||||
|
case Number(number):
|
||||||
|
return Int(number)
|
||||||
|
case TupleExpr(elements_):
|
||||||
|
tuple_elements = [calculate_expression(element, program) for element in elements_]
|
||||||
|
return TupleObject(TupleType([element.get_type() for element in tuple_elements]), tuple(tuple_elements))
|
||||||
|
case Ternary(condition_, if_true, if_false):
|
||||||
|
return calculate_expression(if_true, program) if is_truthy(calculate_expression(condition_, program)) else calculate_expression(if_false, program)
|
||||||
|
case Or(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
assert isinstance(left_value, Bool)
|
||||||
|
if left_value.value: return Bool(True)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(right_value, Bool)
|
||||||
|
return Bool(left_value.value or right_value.value)
|
||||||
|
case And(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
assert isinstance(left_value, Bool)
|
||||||
|
if not left_value.value: return Bool(False)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(right_value, Bool)
|
||||||
|
return Bool(left_value.value and right_value.value)
|
||||||
|
case Bor(lhs, rhs):
|
||||||
|
assert False, ("Unimplemented", lhs, rhs)
|
||||||
|
case Bxor(lhs, rhs):
|
||||||
|
assert False, ("Unimplemented", lhs, rhs)
|
||||||
|
case Band(lhs, rhs):
|
||||||
|
assert False, ("Unimplemented", lhs, rhs)
|
||||||
|
case Equal(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
if left_value.get_type() != right_value.get_type(): return Bool(False)
|
||||||
|
return Bool(left_value == right_value)
|
||||||
|
case NotEqual(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
if left_value.get_type() != right_value.get_type(): return Bool(True)
|
||||||
|
return Bool(left_value != right_value)
|
||||||
|
case LessThan(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(left_value, Int)
|
||||||
|
assert isinstance(right_value, Int)
|
||||||
|
return Bool(left_value.num < right_value.num)
|
||||||
|
case GreaterThan(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(left_value, Int)
|
||||||
|
assert isinstance(right_value, Int)
|
||||||
|
return Bool(left_value.num > right_value.num)
|
||||||
|
case LessThanOrEqual(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(left_value, Int)
|
||||||
|
assert isinstance(right_value, Int)
|
||||||
|
return Bool(left_value.num <= right_value.num)
|
||||||
|
case GreaterThanOrEqual(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(left_value, Int)
|
||||||
|
assert isinstance(right_value, Int)
|
||||||
|
return Bool(left_value.num >= right_value.num)
|
||||||
|
case ShiftLeft(lhs, rhs):
|
||||||
|
assert False, ("Unimplemented", lhs, rhs)
|
||||||
|
case ShiftRight(lhs, rhs):
|
||||||
|
assert False, ("Unimplemented", lhs, rhs)
|
||||||
|
case Addition(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
if isinstance(left_value, Int):
|
||||||
|
assert isinstance(right_value, Int)
|
||||||
|
return Int(left_value.num + right_value.num)
|
||||||
|
elif isinstance(left_value, Str):
|
||||||
|
assert isinstance(right_value, Str)
|
||||||
|
return Str(left_value.str + right_value.str)
|
||||||
|
elif isinstance(left_value, ListObject):
|
||||||
|
assert isinstance(right_value, ListObject)
|
||||||
|
if left_value.type.type == VariableType(""): return right_value
|
||||||
|
if right_value.type.type == VariableType(""): return left_value
|
||||||
|
assert left_value.type == right_value.type, (left_value, right_value)
|
||||||
|
return ListObject(left_value.type, left_value.list + right_value.list)
|
||||||
|
else:
|
||||||
|
assert False, f"Expected two ints or two strs. Got {left_value.get_type().represent()} and {right_value.get_type().represent()}!"
|
||||||
|
case Subtract(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(left_value, Int)
|
||||||
|
assert isinstance(right_value, Int)
|
||||||
|
return Int(left_value.num - right_value.num)
|
||||||
|
case Multiplication(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
assert isinstance(left_value, Int)
|
||||||
|
assert isinstance(right_value, Int)
|
||||||
|
return Int(left_value.num * right_value.num)
|
||||||
|
case Division(lhs, rhs):
|
||||||
|
assert False, ("Unimplemented", lhs, rhs)
|
||||||
|
case Modulo(lhs, rhs):
|
||||||
|
left_value = calculate_expression(lhs, program)
|
||||||
|
right_value = calculate_expression(rhs, program)
|
||||||
|
if isinstance(left_value, Int):
|
||||||
|
assert isinstance(right_value, Int), f"Expected int, got {right_value.get_type().represent()}!"
|
||||||
|
return Int(left_value.num % right_value.num)
|
||||||
|
elif isinstance(left_value, Str):
|
||||||
|
# TODO: Maybe actually just implement C-style string formatting? This code is a mess
|
||||||
|
match right_value:
|
||||||
|
case TupleObject(_, tuple_obj_elements):
|
||||||
|
assert left_value.str.count("%"+"s") == len(tuple_obj_elements), (left_value.str.count("%%s"), len(tuple_obj_elements))
|
||||||
|
the_elements: Tuple[str, ...] = ()
|
||||||
|
for element in tuple_obj_elements:
|
||||||
|
assert isinstance(element, Str)
|
||||||
|
the_elements += (element.str,)
|
||||||
|
|
||||||
|
return Str(left_value.str % the_elements)
|
||||||
|
case Str(string):
|
||||||
|
return Str(left_value.str % string)
|
||||||
|
case _:
|
||||||
|
assert False, f"Format string expected either a string or a tuple of strings, but got a '{right_value.get_type().represent()}'!\n{lhs, rhs, right_value}"
|
||||||
|
match right_value:
|
||||||
|
case Int(num): return Str(left_value.str % num)
|
||||||
|
case _: assert False, ("Unimplemented", right_value)
|
||||||
|
assert False, ("Unimplemented", lhs, rhs)
|
||||||
|
case Return(expression):
|
||||||
|
value = calculate_expression(expression, program)
|
||||||
|
return ReturnObject(ReturnType(value.get_type()), value)
|
||||||
|
case StructInstantiation(struct_, arguments_):
|
||||||
|
struct = calculate_expression(struct_, program)
|
||||||
|
assert isinstance(struct, TypeObject)
|
||||||
|
assert isinstance(struct.type, StructType)
|
||||||
|
|
||||||
|
struct_arguments = {name: calculate_expression(expression, program) for (name, expression) in arguments_}
|
||||||
|
|
||||||
|
for field in struct_arguments:
|
||||||
|
assert field in struct.type.members, f"The struct {struct.type.name} does not have the field '{field}'!"
|
||||||
|
assert struct_arguments[field].get_type().is_subtype_of(struct.type.members[field]), f"'{struct.type.name}.{field}' field expected value of type {struct.type.members[field].represent()}, but got a value of type {struct_arguments[field].get_type().represent()}!"
|
||||||
|
|
||||||
|
for field in struct.type.members:
|
||||||
|
assert field in struct_arguments, f"Missing field '{field}' of type {struct.type.represent()}."
|
||||||
|
|
||||||
|
return Struct(struct.type, struct_arguments)
|
||||||
|
case FieldAccess(expression_, field):
|
||||||
|
value = calculate_expression(expression_, program)
|
||||||
|
match value:
|
||||||
|
case TypeObject(type):
|
||||||
|
match type:
|
||||||
|
case EnumType(name, members, _):
|
||||||
|
assert field in members, f"{type.represent()} does not contain the member '{field}'!"
|
||||||
|
member = members[field]
|
||||||
|
if not member: return EnumValue(type, field, [])
|
||||||
|
def return_member(name: str, parameters: List_[Tuple[str, Type]], return_type: Type, _statement: Statement, *args: Object):
|
||||||
|
assert isinstance(return_type, EnumType)
|
||||||
|
assert len(args) == len(parameters)
|
||||||
|
for (arg, (_, parameter)) in zip(args, parameters):
|
||||||
|
assert arg.get_type().is_subtype_of(parameter)
|
||||||
|
return EnumValue(return_type, name, list(args))
|
||||||
|
return Function(FunctionType(member, type), (field, [('', member_type) for member_type in member], type, Statements([]), return_member))
|
||||||
|
|
||||||
|
case _: assert False, ("Unimplemented", type, field)
|
||||||
|
case Struct(type, fields):
|
||||||
|
assert field in fields, f"Struct '{type.represent()}' does not have the field '{field}'!"
|
||||||
|
return fields[field]
|
||||||
|
case _: assert False, ("Unimplemented", value, field)
|
||||||
|
case ArrayAccess(array_, index_):
|
||||||
|
array = calculate_expression(array_, program)
|
||||||
|
assert array.get_type().is_indexable(), f"Objects of type {array.get_type().represent()} cannot be indexed!"
|
||||||
|
if isinstance(array, Str):
|
||||||
|
index = calculate_expression(index_, program)
|
||||||
|
assert isinstance(index, Int), f"Index must be '{IntType.represent()}', got '{index.get_type().represent()}'!"
|
||||||
|
assert 0 <= index.num < len(array.str), f"Index out of bounds. Str of length {len(array.str)} accessed at index {index.num}. {array_, index_}"
|
||||||
|
return Str(array.str[index.num])
|
||||||
|
elif isinstance(array, TupleObject):
|
||||||
|
index = calculate_expression(index_, program)
|
||||||
|
assert isinstance(index, Int), f"Index must be '{IntType.represent()}', got '{index.get_type().represent()}'!"
|
||||||
|
assert 0 <= index.num <= len(array.tuple), f"Index out of bounds. Tuple of length {len(array.tuple)} accessed at index {index.num}. {array_, index_}"
|
||||||
|
return array.tuple[index.num]
|
||||||
|
elif isinstance(array, ListObject):
|
||||||
|
array_type = array.type.type
|
||||||
|
index = calculate_expression(index_, program)
|
||||||
|
assert isinstance(index, Int), f"Index must be '{IntType.represent()}', got '{index.get_type().represent()}'!"
|
||||||
|
assert 0 <= index.num < len(array.list), f"Index out of bounds. List of length {len(array.list)} accessed at index {index.num}"
|
||||||
|
element = array.list[index.num]
|
||||||
|
assert element.get_type().is_subtype_of(array_type)
|
||||||
|
return element
|
||||||
|
elif isinstance(array, DictionaryObject):
|
||||||
|
index = calculate_expression(index_, program)
|
||||||
|
assert index.get_type().is_subtype_of(array.type.key_type)
|
||||||
|
index_h = index.hash()
|
||||||
|
assert index_h in array.dict, f"{index} is not in {array}! {array_}, {index_}"
|
||||||
|
value = array.dict[index_h]
|
||||||
|
assert value.get_type().is_subtype_of(array.type.value_type)
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
assert False, "Unreachable"
|
||||||
|
case Bnot(expression_):
|
||||||
|
assert False, ("Unimplemented", expression_)
|
||||||
|
case Not(expression_):
|
||||||
|
value = calculate_expression(expression_, program)
|
||||||
|
assert isinstance(value, Bool)
|
||||||
|
return Bool(not value.value)
|
||||||
|
case UnaryPlus(expression_):
|
||||||
|
assert False, ("Unimplemented", expression_)
|
||||||
|
case UnaryMinus (expression_):
|
||||||
|
assert False, ("Unimplemented", expression_)
|
||||||
|
case Array(array_):
|
||||||
|
if len(array_) == 0:
|
||||||
|
return ListObject(ListType(VariableType("")), [])
|
||||||
|
elements_type: Optional[Type] = None
|
||||||
|
array_elements_: List_[Object] = []
|
||||||
|
for element_ in array_:
|
||||||
|
element = calculate_expression(element_, program)
|
||||||
|
if elements_type:
|
||||||
|
assert element.get_type().is_subtype_of(elements_type), (element, elements_type)
|
||||||
|
else:
|
||||||
|
elements_type = element.get_type()
|
||||||
|
array_elements_.append(element)
|
||||||
|
assert elements_type
|
||||||
|
return ListObject(ListType(elements_type), array_elements_)
|
||||||
|
case LoopComprehension(body_, variable, array_):
|
||||||
|
array = calculate_expression(array_, program)
|
||||||
|
assert array.get_type().is_indexable()
|
||||||
|
if isinstance(array, ListObject):
|
||||||
|
elements: List_[Object] = []
|
||||||
|
elements_type = None
|
||||||
|
for element in array.list:
|
||||||
|
program.push_context({variable: Declared.from_obj(element)})
|
||||||
|
elements.append(calculate_expression(body_, program))
|
||||||
|
program.pop_context()
|
||||||
|
if elements_type:
|
||||||
|
assert elements[-1].get_type().is_subtype_of(elements_type)
|
||||||
|
else:
|
||||||
|
elements_type = elements[-1].get_type()
|
||||||
|
|
||||||
|
if not elements: return ListObject(ListType(VariableType("")), [])
|
||||||
|
assert elements_type
|
||||||
|
return ListObject(ListType(elements_type), elements)
|
||||||
|
elif isinstance(array, Dictionary):
|
||||||
|
elements = []
|
||||||
|
elements_type = None
|
||||||
|
for element_h in array.dict:
|
||||||
|
element = element_h.get_object()
|
||||||
|
program.push_context({variable: Declared.from_obj(element)})
|
||||||
|
elements.append(calculate_expression(body_, program))
|
||||||
|
program.pop_context()
|
||||||
|
if elements_type:
|
||||||
|
assert elements[-1].get_type().is_subtype_of(elements_type)
|
||||||
|
else:
|
||||||
|
elements_type = elements[-1].get_type()
|
||||||
|
|
||||||
|
if not elements: return ListObject(ListType(VariableType("")), [])
|
||||||
|
assert elements_type
|
||||||
|
return ListObject(ListType(elements_type), elements)
|
||||||
|
else:
|
||||||
|
assert False, ("Unimplemented", array)
|
||||||
|
case DictionaryExpr(dict_):
|
||||||
|
dict: Dict[Hashable, Object] = {}
|
||||||
|
if not dict_:
|
||||||
|
return Dictionary(DictionaryType(VariableType(""), VariableType("")), {})
|
||||||
|
key_type, value_type = None, None
|
||||||
|
for (key_, value_) in dict_:
|
||||||
|
key = calculate_expression(key_, program)
|
||||||
|
value = calculate_expression(value_, program)
|
||||||
|
if key_type:
|
||||||
|
assert value_type, "Unreachable"
|
||||||
|
assert key.get_type().is_subtype_of(key_type)
|
||||||
|
assert value.get_type().is_subtype_of(value_type)
|
||||||
|
else:
|
||||||
|
assert not value_type
|
||||||
|
key_type = key.get_type()
|
||||||
|
value_type = value.get_type()
|
||||||
|
dict[key.hash()] = value
|
||||||
|
assert key_type and value_type
|
||||||
|
assert not (isinstance(key_type, VariableType) and key_type.name == '')
|
||||||
|
return Dictionary(DictionaryType(key_type, value_type), dict)
|
||||||
|
case DictComprehension(body_, variable, array_):
|
||||||
|
array = calculate_expression(array_, program)
|
||||||
|
assert array.get_type().is_indexable()
|
||||||
|
if isinstance(array, ListObject):
|
||||||
|
key_, value_ = body_
|
||||||
|
dict_entries: Dict[Hashable, Object] = {}
|
||||||
|
key_type = None
|
||||||
|
value_type = None
|
||||||
|
for element in array.list:
|
||||||
|
program.push_context({variable: Declared.from_obj(element)})
|
||||||
|
key = calculate_expression(key_, program)
|
||||||
|
key_h = key.hash()
|
||||||
|
dict_entries[key_h] = calculate_expression(value_, program)
|
||||||
|
program.pop_context()
|
||||||
|
if key_type:
|
||||||
|
assert value_type
|
||||||
|
assert key.get_type().is_subtype_of(key_type)
|
||||||
|
assert dict_entries[key_h].get_type().is_subtype_of(value_type)
|
||||||
|
else:
|
||||||
|
assert not value_type
|
||||||
|
key_type = key.get_type()
|
||||||
|
value_type = dict_entries[key_h].get_type()
|
||||||
|
if not dict_entries: return Dictionary(DictionaryType(VariableType(""), VariableType("")), {})
|
||||||
|
assert key_type and value_type
|
||||||
|
assert not (isinstance(key_type, VariableType) and key_type.name == '')
|
||||||
|
return Dictionary(DictionaryType(key_type, value_type), dict_entries)
|
||||||
|
case _:
|
||||||
|
assert False, ("Unimplemented", expression)
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def calculate_type_expression(expression: TypeExpression, program: ProgramState, must_resolve:bool=True) -> Type:
|
||||||
|
match expression:
|
||||||
|
case TypeName(name):
|
||||||
|
if not program.exists(name) and not must_resolve: return VariableType(name)
|
||||||
|
type_obj = program.access_variable(name)
|
||||||
|
assert isinstance(type_obj, TypeObject)
|
||||||
|
return type_obj.type
|
||||||
|
case ListTypeExpr(type_):
|
||||||
|
return ListType(calculate_type_expression(type_, program, must_resolve))
|
||||||
|
case TupleTypeExpr(types_):
|
||||||
|
return TupleType([calculate_type_expression(type, program, must_resolve) for type in types_])
|
||||||
|
case UnionTypeExpr(types_):
|
||||||
|
return UnionType([calculate_type_expression(type, program, must_resolve) for type in types_])
|
||||||
|
case FunctionTypeExpr(arguments_, return_type_):
|
||||||
|
return FunctionType([calculate_type_expression(argument, program, must_resolve) for argument in arguments_], calculate_type_expression(return_type_, program, must_resolve))
|
||||||
|
case TypeSpecification(type_, types_):
|
||||||
|
type = calculate_type_expression(type_, program, must_resolve)
|
||||||
|
assert isinstance(type, GenericType)
|
||||||
|
assert len(type.variables) == len(types_)
|
||||||
|
types = [calculate_type_expression(type_, program, must_resolve) for type_ in types_]
|
||||||
|
result_type = type.substitute(types)
|
||||||
|
return result_type
|
||||||
|
case _:
|
||||||
|
assert False, ("Unimplemented", expression)
|
||||||
|
assert False, "Unreachable"
|
||||||
|
|
||||||
|
def match_enum_expression(enum: Type, value: Object, expression: Expression) -> Optional[Dict[str, Object]]:
|
||||||
|
assert isinstance(enum, EnumType)
|
||||||
|
assert isinstance(value, EnumValue)
|
||||||
|
match expression:
|
||||||
|
case Variable(name):
|
||||||
|
if name.startswith('_'): return {name: value}
|
||||||
|
assert name in enum.members, f"Enum '{enum.represent()}' does not contain the member '{name}'!"
|
||||||
|
assert enum.members[name] == [], f"Enum member '{enum.represent()}.{name}' has {len(enum.members[name])} fields that have not been captured!"
|
||||||
|
if name != value.name or value.values: return None
|
||||||
|
return {}
|
||||||
|
case FunctionCall(function, arguments):
|
||||||
|
assert isinstance(function, Variable)
|
||||||
|
assert function.name in enum.members, f"Enum '{enum.represent()}' does not contain the member '{function.name}'!"
|
||||||
|
if function.name != value.name: return None
|
||||||
|
member = enum.members[function.name]
|
||||||
|
assert isinstance(member, list) # TODO: Report calling a struct enum member with parentheses
|
||||||
|
assert isinstance(value.values, list) # Same as above but like inverse
|
||||||
|
assert len(arguments) == len(member), f"{value.get_type().represent()}.{value.name} expected {len(member)} args, but got {len(arguments)}!"
|
||||||
|
assert len(member) == len(value.values)
|
||||||
|
new_variables: Dict[str, Object] = {}
|
||||||
|
for argument, element in zip(arguments, value.values):
|
||||||
|
assert isinstance(argument, Variable) # TODO
|
||||||
|
new_variables[argument.name] = element
|
||||||
|
return new_variables
|
||||||
|
case _:
|
||||||
|
assert False, ("Unimplemented", expression)
|
||||||
|
assert False, ("Unimplemented", value, expression)
|
||||||
|
|
||||||
|
def update_types(type: Type, program: ProgramState):
|
||||||
|
assert isinstance(type, EnumType) or isinstance(type, StructType)
|
||||||
|
for context in program.contexts:
|
||||||
|
for variable_ in context:
|
||||||
|
variable = context[variable_]
|
||||||
|
match variable:
|
||||||
|
case Declared(type_, value):
|
||||||
|
if isinstance(variable.value, TypeObject):
|
||||||
|
assert type_ == TypeType
|
||||||
|
assert isinstance(value, TypeObject)
|
||||||
|
value.type.fill({type.name: type}, [])
|
||||||
|
case Undeclared(type_): pass
|
||||||
|
case _: assert False, ("Unimplemented", variable)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ReturnResult:
|
||||||
|
value: Object
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ContinueResult:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BreakResult:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NothingResult:
|
||||||
|
pass
|
||||||
|
|
||||||
|
StatementsResult = Union[ReturnResult, ContinueResult, BreakResult, NothingResult]
|
||||||
|
|
||||||
|
def interpret_statements(statements: List_[Statement], program: ProgramState) -> StatementsResult:
|
||||||
|
for statement in statements:
|
||||||
|
match statement:
|
||||||
|
case ExpressionStatement(expression):
|
||||||
|
value = calculate_expression(expression, program)
|
||||||
|
if isinstance(value, ReturnObject): return ReturnResult(value.value)
|
||||||
|
case Assignment(lhs, rhs, type_):
|
||||||
|
assert is_valid_target(lhs)
|
||||||
|
match lhs:
|
||||||
|
case Variable(name):
|
||||||
|
value = calculate_expression(rhs, program)
|
||||||
|
if type_:
|
||||||
|
type = calculate_type_expression(type_, program)
|
||||||
|
program.declare_variable(name, type)
|
||||||
|
program.assign_variable(name, value)
|
||||||
|
case FieldAccess(expression_, field):
|
||||||
|
expr = calculate_expression(expression_, program)
|
||||||
|
assert isinstance(expr, Struct)
|
||||||
|
struct_type = expr.get_type()
|
||||||
|
assert isinstance(struct_type, StructType)
|
||||||
|
assert field in struct_type.members, f"Struct '{struct_type.represent()}' does not contain the field '{field}'!"
|
||||||
|
value = calculate_expression(rhs, program)
|
||||||
|
assert value.get_type().is_subtype_of(struct_type.members[field])
|
||||||
|
expr.fields[field] = value
|
||||||
|
case ArrayAccess(array_, index_):
|
||||||
|
array = calculate_expression(array_, program)
|
||||||
|
index = calculate_expression(index_, program)
|
||||||
|
value = calculate_expression(rhs, program)
|
||||||
|
assert array.get_type().is_indexable(), array
|
||||||
|
match array:
|
||||||
|
case Dictionary(dict_type, dict_):
|
||||||
|
try:
|
||||||
|
index_h = index.hash()
|
||||||
|
except AssertionError:
|
||||||
|
assert False, (array_, index_, index, dict_)
|
||||||
|
if isinstance(dict_type.key_type, VariableType) and dict_type.key_type.name == "":
|
||||||
|
dict_type.key_type, dict_type.value_type = index.get_type(), value.get_type()
|
||||||
|
assert index.get_type().is_subtype_of(dict_type.key_type), (index, dict_type.key_type)
|
||||||
|
assert value.get_type().is_subtype_of(dict_type.value_type), (value, dict_type.value_type)
|
||||||
|
|
||||||
|
dict_[index_h] = value
|
||||||
|
case _: assert False, ("Unimplemented", array)
|
||||||
|
case _:
|
||||||
|
assert False, ("Unimplemented", lhs)
|
||||||
|
case IfStatement(condition, body, else_body):
|
||||||
|
if is_truthy(calculate_expression(condition, program)):
|
||||||
|
program.push_context({})
|
||||||
|
return_value = interpret_statements([body], program)
|
||||||
|
program.pop_context()
|
||||||
|
if not isinstance(return_value, NothingResult): return return_value
|
||||||
|
elif else_body:
|
||||||
|
program.push_context({})
|
||||||
|
return_value = interpret_statements([else_body], program)
|
||||||
|
program.pop_context()
|
||||||
|
if not isinstance(return_value, NothingResult): return return_value
|
||||||
|
case Statements(statements):
|
||||||
|
# TODO: Proper context and scoping
|
||||||
|
program.push_context({})
|
||||||
|
return_value = interpret_statements(statements, program)
|
||||||
|
program.pop_context()
|
||||||
|
if not isinstance(return_value, NothingResult): return return_value
|
||||||
|
case FunctionDefinition(name, arguments_, return_type_, body):
|
||||||
|
def run_function(name: str, arguments: List_[Tuple[str, Type]], return_type: Type, body: Statement, *args: Object) -> Object:
|
||||||
|
assert len(args) == len(arguments), f"'{name}' expected {len(arguments)} arguments, but got {len(args)} instead!"
|
||||||
|
new_program = ProgramState(program.modules, program.contexts[:2])
|
||||||
|
new_program.push_context({})
|
||||||
|
for (argument, (argument_name, argument_type)) in zip(args, arguments):
|
||||||
|
assert argument.get_type().is_subtype_of(argument_type), f"'{name}' expected argument '{argument_name}' to have a value of type {argument_type.represent()}, but got {argument.get_type().represent()} instead!"
|
||||||
|
new_program.declare_variable(argument_name, argument_type)
|
||||||
|
new_program.assign_variable(argument_name, argument)
|
||||||
|
return_value = interpret_statements([body], new_program)
|
||||||
|
new_program.pop_context()
|
||||||
|
assert len(new_program.contexts) == 2
|
||||||
|
match return_value:
|
||||||
|
case ReturnResult(value):
|
||||||
|
assert value.get_type().is_subtype_of(return_type), f"'{name}' expected a return value of type {return_type.represent()}, but got {value.get_type().represent()}!"
|
||||||
|
return value
|
||||||
|
case NothingResult():
|
||||||
|
assert return_type.is_subtype_of(VoidType), f"'{name}' expected a return type of {return_type.represent()} but got nothing!"
|
||||||
|
return Void
|
||||||
|
case _: assert False, ("Unimplemented", return_value)
|
||||||
|
|
||||||
|
arguments = [(argument.name, calculate_type_expression(argument.type, program)) for argument in arguments_]
|
||||||
|
return_type = calculate_type_expression(return_type_, program) if return_type_ else VoidType
|
||||||
|
function_type = FunctionType([argument[1] for argument in arguments], return_type)
|
||||||
|
object = Function(function_type, (name, arguments, return_type, body, run_function))
|
||||||
|
|
||||||
|
program.declare_and_assign_variable(name, object)
|
||||||
|
case EnumDefinition(name, entries):
|
||||||
|
enum_type = EnumType(name, {entry.name: [calculate_type_expression(type, program, False) for type in entry.types] for entry in entries}, [])
|
||||||
|
program.declare_and_assign_variable(name, TypeObject(enum_type))
|
||||||
|
update_types(enum_type, program)
|
||||||
|
case StructDefinition(name, entries):
|
||||||
|
struct_type = StructType(name, {entry.name: calculate_type_expression(entry.type, program, False) for entry in entries}, [])
|
||||||
|
program.declare_and_assign_variable(name, TypeObject(struct_type))
|
||||||
|
update_types(struct_type, program)
|
||||||
|
case MatchStatement(value_, cases):
|
||||||
|
value = calculate_expression(value_, program)
|
||||||
|
assert isinstance(value, EnumValue), f"Cannot only match over enums, got {value.get_type().represent()} instead!"
|
||||||
|
assert isinstance(value.type, EnumType) # TODO: Pattern match things besides enums
|
||||||
|
|
||||||
|
for case in cases:
|
||||||
|
if (new_variables := match_enum_expression(value.type, value, case[0])) is not None:
|
||||||
|
program.push_context({name: Declared.from_obj(new_variables[name]) for name in new_variables})
|
||||||
|
return_value = interpret_statements([case[1]], program)
|
||||||
|
program.pop_context()
|
||||||
|
if not isinstance(return_value, NothingResult): return return_value
|
||||||
|
break
|
||||||
|
case DoWhileStatement(body, condition_):
|
||||||
|
assert condition_ is None # TODO
|
||||||
|
program.push_context({})
|
||||||
|
return_value = interpret_statements([body], program)
|
||||||
|
program.pop_context()
|
||||||
|
if not isinstance(return_value, NothingResult): return return_value
|
||||||
|
case WhileStatement(condition_, body):
|
||||||
|
while is_truthy(calculate_expression(condition_, program)):
|
||||||
|
program.push_context({})
|
||||||
|
return_value = interpret_statements([body], program)
|
||||||
|
program.pop_context()
|
||||||
|
match return_value:
|
||||||
|
case NothingResult(): pass
|
||||||
|
case ContinueResult(): continue
|
||||||
|
case BreakResult(): break
|
||||||
|
case ReturnResult(_): return return_value
|
||||||
|
case _: assert False, ("Unimplemented", return_value)
|
||||||
|
if not isinstance(return_value, NothingResult): return return_value
|
||||||
|
case AssertStatement(condition_, message_):
|
||||||
|
if not is_truthy(calculate_expression(condition_, program)):
|
||||||
|
if message_:
|
||||||
|
message = calculate_expression(message_, program)
|
||||||
|
assert isinstance(message, Str)
|
||||||
|
assert False, message.str
|
||||||
|
assert False, "Assertion failed"
|
||||||
|
case TypeDeclarationStatement(declaration):
|
||||||
|
program.declare_variable(declaration.name, calculate_type_expression(declaration.type, program))
|
||||||
|
case ForLoop(variable, array_, body):
|
||||||
|
array = calculate_expression(array_, program)
|
||||||
|
if isinstance(array, ListObject):
|
||||||
|
for value in array.list:
|
||||||
|
assert isinstance(value, Object)
|
||||||
|
assert value.get_type().is_subtype_of(array.type.type)
|
||||||
|
program.push_context({variable: Declared.from_obj(value)})
|
||||||
|
return_value = interpret_statements([body], program)
|
||||||
|
program.pop_context()
|
||||||
|
match return_value:
|
||||||
|
case NothingResult(): pass
|
||||||
|
case ReturnResult(_): return return_value
|
||||||
|
case _: assert False, ("Unimplemented", return_value)
|
||||||
|
elif isinstance(array, Dictionary):
|
||||||
|
for value_h in array.dict:
|
||||||
|
value = value_h.get_object()
|
||||||
|
assert value.get_type().is_subtype_of(array.type.key_type)
|
||||||
|
program.push_context({variable: Declared.from_obj(value)})
|
||||||
|
return_value = interpret_statements([body], program)
|
||||||
|
program.pop_context()
|
||||||
|
match return_value:
|
||||||
|
case NothingResult(): pass
|
||||||
|
case ReturnResult(_): return return_value
|
||||||
|
case _: assert False, ("Unimplemented", return_value)
|
||||||
|
case ContinueStatement(): return ContinueResult()
|
||||||
|
case BreakStatement(): return BreakResult()
|
||||||
|
case Import(file_):
|
||||||
|
# TODO: Maybe an inclusion system within a preprocessor maybe
|
||||||
|
file = calculate_expression(file_, program)
|
||||||
|
assert isinstance(file, Str), "Only strings are valid file paths!"
|
||||||
|
module = interpret_file(file.str, program.modules) if file.str not in program.modules else program.modules[file.str]
|
||||||
|
program.contexts[0] |= module
|
||||||
|
if file.str not in program.modules:
|
||||||
|
program.modules[file.str] = module
|
||||||
|
case TypeDefinition(name, expression_):
|
||||||
|
program.declare_and_assign_variable(name, TypeObject(calculate_type_expression(expression_, program)))
|
||||||
|
case _:
|
||||||
|
assert False, ("Unimplemented", statement)
|
||||||
|
return NothingResult()
|
||||||
|
|
||||||
|
def interpret_file(file_path: str, modules: Dict[str, Module]) -> Module:
|
||||||
|
# print(f"\tParsing {file_path}")
|
||||||
|
lexer = Lexer.from_file(file_path)
|
||||||
|
statements: List_[Statement] = []
|
||||||
|
while not lexer.check_token(EofToken()): statements.append(parse_statement(lexer))
|
||||||
|
# print(f"\tInterpreting {file_path}")
|
||||||
|
program = ProgramState(modules, [{variable: Declared.from_obj(variables[variable]) for variable in variables}, {}])
|
||||||
|
return_value = interpret_statements(statements, program)
|
||||||
|
# print(f"Finished {file_path}")
|
||||||
|
assert len(program.contexts) == 2
|
||||||
|
match return_value:
|
||||||
|
case NothingResult(): pass
|
||||||
|
case ReturnObject(_): assert False, "Cannot return from outside a function!"
|
||||||
|
case ContinueResult(): assert False, "Cannot continue from outside a loop!"
|
||||||
|
case BreakResult(): assert False, "Cannot break from outside a loop!"
|
||||||
|
case _: assert False, ("Unimplemented", return_value)
|
||||||
|
return program.contexts[1]
|
155
ppp_lexer.ppp
Normal file
155
ppp_lexer.ppp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import "ppp_tokens.ppp";
|
||||||
|
|
||||||
|
struct Lexer {
|
||||||
|
source: str,
|
||||||
|
location: int,
|
||||||
|
line: int,
|
||||||
|
col: int,
|
||||||
|
peeked_token: OptionalToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func new_lexer(source: str) -> Lexer {
|
||||||
|
return Lexer{
|
||||||
|
source = source,
|
||||||
|
location = 0,
|
||||||
|
line = 1,
|
||||||
|
col = 0,
|
||||||
|
peeked_token = OptionalToken.None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_from_file(path: str) -> Lexer return new_lexer(read(path));
|
||||||
|
|
||||||
|
func is_space(char: str) -> bool {
|
||||||
|
return char == " " || char == "\t" || char == "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_digit(char: str) -> bool {
|
||||||
|
return char == "0" || char == "1" || char == "2" || char == "3" || char == "4" || char == "5" || char == "6" || char == "7" || char == "8" || char == "9";
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_alpha(char: str) -> bool {
|
||||||
|
return char == "a" || char == "b" || char == "c" || char == "d" || char == "e" || char == "f" || char == "g" || char == "h" || char == "i" || char == "j" || char == "k" || char == "l" || char == "m" || char == "n" || char == "o" || char == "p" || char == "q" || char == "r" || char == "s" || char == "t" || char == "u" || char == "v" || char == "w" || char == "x" || char == "y" || char == "z" || char == "A" || char == "B" || char == "C" || char == "D" || char == "E" || char == "F" || char == "G" || char == "H" || char == "I" || char == "J" || char == "K" || char == "L" || char == "M" || char == "N" || char == "O" || char == "P" || char == "Q" || char == "R" || char == "S" || char == "T" || char == "U" || char == "V" || char == "W" || char == "X" || char == "Y" || char == "Z" || char == "_";
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_next_token(lexer: Lexer) -> Token {
|
||||||
|
match lexer.peeked_token in {
|
||||||
|
case Some(token) do {
|
||||||
|
lexer.peeked_token = OptionalToken.None;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while lexer.location < len(lexer.source) && is_space(lexer.source[lexer.location]) do {
|
||||||
|
if lexer.source[lexer.location] == "\n" do {
|
||||||
|
lexer.line = lexer.line + 1;
|
||||||
|
lexer.col = 0;
|
||||||
|
}
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if lexer.location >= len(lexer.source) return Token{line=lexer.line, col=lexer.col, value="\0", contents=TokenContents.Eof};
|
||||||
|
|
||||||
|
if is_digit(lexer.source[lexer.location]) do {
|
||||||
|
number_str: str = "";
|
||||||
|
while lexer.location < len(lexer.source) && is_digit(lexer.source[lexer.location]) do {
|
||||||
|
number_str = number_str + lexer.source[lexer.location];
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
number: int = str_to_int(number_str);
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value=number_str, contents=TokenContents.Number(number)};
|
||||||
|
} else if is_alpha(lexer.source[lexer.location]) do {
|
||||||
|
word_str: str = "";
|
||||||
|
while lexer.location < len(lexer.source) && is_alpha(lexer.source[lexer.location]) do {
|
||||||
|
word_str = word_str + lexer.source[lexer.location];
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
match keyword_from_str(word_str) in {
|
||||||
|
case Some(keyword) return Token{line=lexer.line, col=lexer.col, value=word_str, contents=TokenContents.Keyword(keyword)};
|
||||||
|
case None return Token{line=lexer.line, col=lexer.col, value=word_str, contents=TokenContents.Identifier(word_str)};
|
||||||
|
}
|
||||||
|
assert false, "Identifier";
|
||||||
|
} else if lexer.source[lexer.location] == "\"" do {
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
string_str: str = "";
|
||||||
|
escaping: bool = false;
|
||||||
|
while lexer.location < len(lexer.source) && (lexer.source[lexer.location] != "\"" || escaping) do {
|
||||||
|
escaping = escaping? false: lexer.source[lexer.location] == "\\";
|
||||||
|
string_str = string_str + lexer.source[lexer.location];
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
}
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="\""+string_str+"\"", contents=TokenContents.String(string_str)};
|
||||||
|
} else if lexer.source[lexer.location] == "|" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "|" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="||", contents=TokenContents.Symbol(Symbol.Dpipe)};
|
||||||
|
} else if lexer.source[lexer.location] == "&" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "&" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="&&", contents=TokenContents.Symbol(Symbol.Dampersand)};
|
||||||
|
} else if lexer.source[lexer.location] == "*" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "*" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="**", contents=TokenContents.Symbol(Symbol.Dasterisk)};
|
||||||
|
} else if lexer.source[lexer.location] == "-" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == ">" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="->", contents=TokenContents.Symbol(Symbol.Arrow)};
|
||||||
|
} else if lexer.source[lexer.location] == ">" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value=">=", contents=TokenContents.Symbol(Symbol.GreaterEqual)};
|
||||||
|
} else if lexer.source[lexer.location] == "<" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="<=", contents=TokenContents.Symbol(Symbol.LesserEqual)};
|
||||||
|
} else if lexer.source[lexer.location] == "=" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="==", contents=TokenContents.Symbol(Symbol.Dequal)};
|
||||||
|
} else if lexer.source[lexer.location] == "!" && lexer.location < len(lexer.source)-1 && lexer.source[lexer.location+1] == "=" do {
|
||||||
|
lexer.location = lexer.location + 2;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value="!=", contents=TokenContents.Symbol(Symbol.NotEqual)};
|
||||||
|
} else {
|
||||||
|
match symbol_from_str(lexer.source[lexer.location]) in {
|
||||||
|
case Some(symbol) do {
|
||||||
|
lexer.location = lexer.location + 1;
|
||||||
|
return Token{line=lexer.line, col=lexer.col, value=lexer.source[lexer.location-1], contents=TokenContents.Symbol(symbol)};
|
||||||
|
}
|
||||||
|
case None assert False, "Unimplemented, '%s'" % lexer.source[lexer.location];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_peek_token(lexer: Lexer) -> Token {
|
||||||
|
match lexer.peeked_token in {
|
||||||
|
case Some(token) return token;
|
||||||
|
case None do {
|
||||||
|
token: Token = lexer_next_token(lexer);
|
||||||
|
lexer.peeked_token = OptionalToken.Some(token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_check_token(lexer: Lexer, expected: TokenContents) -> bool {
|
||||||
|
token: Token = lexer_peek_token(lexer);
|
||||||
|
return token.contents == expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_take_token(lexer: Lexer, token: TokenContents) -> OptionalToken {
|
||||||
|
if lexer_check_token(lexer, token) return OptionalToken.Some(lexer_next_token(lexer));
|
||||||
|
return OptionalToken.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_take_tokens(lexer: Lexer, tokens: TokenContents[]) -> OptionalToken {
|
||||||
|
for token in tokens do {
|
||||||
|
if lexer_check_token(lexer, token) return OptionalToken.Some(lexer_next_token(lexer));
|
||||||
|
}
|
||||||
|
return OptionalToken.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_assert_token(lexer: Lexer, expected: TokenContents) -> Token {
|
||||||
|
token: Token = lexer_next_token(lexer);
|
||||||
|
assert token.contents == expected, "Expected %s but got %s!" % (token_contents_to_str(expected), token_to_str(token));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexer_check_tokens(lexer: Lexer, tokens: TokenContents[]) -> bool {
|
||||||
|
for token in tokens if lexer_check_token(lexer, token) return true;
|
||||||
|
return false;
|
||||||
|
}
|
147
ppp_lexer.py
Normal file
147
ppp_lexer.py
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from ppp_tokens import EofToken, IdentifierToken, Keyword, KeywordToken, NumberToken, StringToken, Symbol, SymbolToken, Token, TokenContents
|
||||||
|
|
||||||
|
class Lexer:
|
||||||
|
def __init__(self, source: str) -> None:
|
||||||
|
self._source = source
|
||||||
|
self._location = 0
|
||||||
|
self._line = 1
|
||||||
|
self._col = 0
|
||||||
|
self._peeked_token: Optional[Token] = None
|
||||||
|
self._current: str = ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_file(cls, path: str) -> 'Lexer':
|
||||||
|
with open(path) as f:
|
||||||
|
return cls(f.read())
|
||||||
|
|
||||||
|
def _advance(self) -> str:
|
||||||
|
assert self._location < len(self._source)
|
||||||
|
self._line, self._col = (self._line + 1, 0) if self._current == '\n' else (self._line, self._col + 1)
|
||||||
|
self._location += 1
|
||||||
|
self._current = self._source[self._location] if self._location < len(self._source) else ''
|
||||||
|
return self._current
|
||||||
|
|
||||||
|
# def _peek(self) -> str:
|
||||||
|
# assert self._location < len(self._source)-1
|
||||||
|
|
||||||
|
def next_token(self) -> Token:
|
||||||
|
if self._peeked_token is not None:
|
||||||
|
peeked_token, self._peeked_token = self._peeked_token, None
|
||||||
|
return peeked_token
|
||||||
|
|
||||||
|
while self._location < len(self._source) and self._source[self._location] in ' \t\n': self._advance()
|
||||||
|
|
||||||
|
if self._location >= len(self._source): return Token(self._line, self._col, '\0', EofToken())
|
||||||
|
|
||||||
|
match self._source[self._location]:
|
||||||
|
case c if c.isdigit():
|
||||||
|
start_location = self._location
|
||||||
|
while self._location < len(self._source) and self._source[self._location].isdigit(): self._location += 1
|
||||||
|
number = int(self._source[start_location:self._location])
|
||||||
|
return Token(self._line, self._col, self._source[start_location:self._location], NumberToken(number))
|
||||||
|
case c if c.isalpha() or c == "_":
|
||||||
|
start_location = self._location
|
||||||
|
while self._location < len(self._source) and (self._source[self._location].isalpha() or self._source[self._location] in '_'): self._location += 1
|
||||||
|
word = self._source[start_location:self._location]
|
||||||
|
try:
|
||||||
|
keyword = Keyword(word)
|
||||||
|
return Token(self._line, self._col, word, KeywordToken(keyword))
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
symbol = Symbol(word)
|
||||||
|
return Token(self._line, self._col, word, SymbolToken(symbol))
|
||||||
|
except ValueError:
|
||||||
|
return Token(self._line, self._col, word, IdentifierToken(word))
|
||||||
|
case '"':
|
||||||
|
# TODO: Escaping
|
||||||
|
self._location += 1
|
||||||
|
start_location = self._location
|
||||||
|
escaping = False
|
||||||
|
while self._location < len(self._source) and (self._source[self._location] != '"' or escaping):
|
||||||
|
escaping = self._source[self._location] == '\\' if not escaping else False
|
||||||
|
self._location += 1
|
||||||
|
string = self._source[start_location:self._location].encode('utf-8').decode('unicode_escape')
|
||||||
|
self._location += 1
|
||||||
|
return Token(self._line, self._col, self._source[start_location-1:self._location], StringToken(string))
|
||||||
|
# TODO: Make a proper Trie for this.
|
||||||
|
case '|' if self._location < len(self._source)-1 and self._source[self._location+1] == '|':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.Dpipe))
|
||||||
|
case '&' if self._location < len(self._source)-1 and self._source[self._location+1] == '&':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.Dampersand))
|
||||||
|
case '*' if self._location < len(self._source)-1 and self._source[self._location+1] == '*':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.Dasterisk))
|
||||||
|
case '-' if self._location < len(self._source)-1 and self._source[self._location+1] == '>':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.Arrow))
|
||||||
|
case '>' if self._location < len(self._source)-1 and self._source[self._location+1] == '=':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.GreaterEqual))
|
||||||
|
case '<' if self._location < len(self._source)-1 and self._source[self._location+1] == '=':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.LesserEqual))
|
||||||
|
case '=' if self._location < len(self._source)-1 and self._source[self._location+1] == '=':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.Dequal))
|
||||||
|
case '=' if self._location < len(self._source)-1 and self._source[self._location+1] == '>':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.EqualArrow))
|
||||||
|
case '!' if self._location < len(self._source)-1 and self._source[self._location+1] == '=':
|
||||||
|
self._location += 2
|
||||||
|
return Token(self._line, self._col, self._source[self._location-2:self._location], SymbolToken(Symbol.NotEqual))
|
||||||
|
case c if c in Symbol._value2member_map_:
|
||||||
|
self._location += 1
|
||||||
|
return Token(self._line, self._col, self._source[self._location-1], SymbolToken(Symbol(c)))
|
||||||
|
case _:
|
||||||
|
assert False, ("Unimplemented", c, self._location)
|
||||||
|
assert False, "Unreachable"
|
||||||
|
|
||||||
|
def peek_token(self) -> Token:
|
||||||
|
if self._peeked_token is not None: return self._peeked_token
|
||||||
|
self._peeked_token = self.next_token()
|
||||||
|
return self._peeked_token
|
||||||
|
|
||||||
|
def assert_tokenkind(self, kind: type) -> Token:
|
||||||
|
token = self.next_token()
|
||||||
|
assert isinstance(token.contents, kind), (f"Expected {kind} but got {token.contents}!", self.next_token(), self.next_token(), self.next_token())
|
||||||
|
return token
|
||||||
|
|
||||||
|
def assert_token(self, expected: TokenContents) -> Token:
|
||||||
|
token = self.next_token()
|
||||||
|
assert token.contents == expected, (f"Expected {expected} but got {token.contents}!", self.next_token(), self.next_token())
|
||||||
|
return token
|
||||||
|
|
||||||
|
def check_token(self, expected: TokenContents) -> bool:
|
||||||
|
token = self.peek_token()
|
||||||
|
return token.contents == expected
|
||||||
|
|
||||||
|
def check_tokens(self, *expected: TokenContents) -> bool:
|
||||||
|
for token in expected:
|
||||||
|
if self.check_token(token):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_tokenkind(self, kind: type) -> bool:
|
||||||
|
token = self.peek_token()
|
||||||
|
return isinstance(token.contents, kind)
|
||||||
|
|
||||||
|
def take_tokenkind(self, kind: type) -> Optional[Token]:
|
||||||
|
if self.check_tokenkind(kind):
|
||||||
|
return self.next_token()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def take_token(self, token: TokenContents) -> Optional[Token]:
|
||||||
|
if self.check_token(token):
|
||||||
|
return self.next_token()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def take_tokens(self, *tokens: TokenContents) -> Optional[Token]:
|
||||||
|
for token in tokens:
|
||||||
|
if self.check_token(token):
|
||||||
|
return self.next_token()
|
||||||
|
return None
|
29
ppp_object.ppp
Normal file
29
ppp_object.ppp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import "ppp_types.ppp";
|
||||||
|
import "ppp_ast.ppp";
|
||||||
|
|
||||||
|
enum Object {
|
||||||
|
Int(int),
|
||||||
|
Str(str),
|
||||||
|
Bool(bool),
|
||||||
|
Void,
|
||||||
|
Type(Type),
|
||||||
|
Tuple(Type, Object[]),
|
||||||
|
List(Type, Object[]),
|
||||||
|
Array(Type, int, Object[]),
|
||||||
|
Function(Type, (str, (str, Type)[], Type, Statement, (str, (str, Type)[], Type, Statement, Object[]) -> Object)),
|
||||||
|
Return(Type, Object),
|
||||||
|
EnumValue(Type, str, (dict[str, Object] | Object[])),
|
||||||
|
EnumStruct(Type, dict[str, Object]),
|
||||||
|
Struct(Type, dict[str, Object])
|
||||||
|
}
|
||||||
|
|
||||||
|
func object_get_type(object: Object) -> Type {
|
||||||
|
match object in {
|
||||||
|
case Int(_) return Type.Int;
|
||||||
|
case Str(_) return Type.Str;
|
||||||
|
case Type(_) return Type.Type;
|
||||||
|
case Function(type_, _) return type_;
|
||||||
|
case EnumValue(type_, _, _) return type_;
|
||||||
|
case _ assert false, "Unimplemented object_get_type %s" % object;
|
||||||
|
}
|
||||||
|
}
|
141
ppp_object.py
Normal file
141
ppp_object.py
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# This file exists because I wanted to keep ppp_stdlib.py and ppp_interpreter.py seperate but they both rely on this one class.
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Callable, Dict, List as List_, Tuple as Tuple_, Union as Union_
|
||||||
|
|
||||||
|
from ppp_ast import Statement
|
||||||
|
from ppp_types import ArrayType, DictionaryType, EnumType, FunctionType, ListType, ReturnType, StructType, TupleType, Type, Int as IntType, Str as StrType, Bool as BoolType, Void as VoidType, TypeType
|
||||||
|
|
||||||
|
class Object(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def get_type(self) -> Type: ...
|
||||||
|
|
||||||
|
def hash(self) -> 'Hashable':
|
||||||
|
assert False, f"{self.get_type().represent()} cannot be hashed."
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Int(Object):
|
||||||
|
num: int
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return IntType
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Str(Object):
|
||||||
|
str: str
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return StrType
|
||||||
|
|
||||||
|
def hash(self) -> 'HStr':
|
||||||
|
return HStr(self.str)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Bool(Object):
|
||||||
|
value: bool
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return BoolType
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Void_(Object):
|
||||||
|
def get_type(self) -> Type: return VoidType
|
||||||
|
Void = Void_()
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TypeObject(Object):
|
||||||
|
type: Type
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return TypeType
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Tuple(Object):
|
||||||
|
type: TupleType
|
||||||
|
tuple: Tuple_[Object, ...]
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class List(Object):
|
||||||
|
type: ListType
|
||||||
|
list: List_[Object]
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Array(Object):
|
||||||
|
type: ArrayType
|
||||||
|
array: List_[Object]
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Function(Object):
|
||||||
|
type: FunctionType
|
||||||
|
function: Tuple_[str, List_[Tuple_[str, Type]], Type, Statement, Callable[..., Object]]
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Return(Object):
|
||||||
|
type: ReturnType
|
||||||
|
value: Object
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EnumValue(Object):
|
||||||
|
type: EnumType
|
||||||
|
name: str
|
||||||
|
values: List_[Object]
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
def hash(self) -> 'HEnumValue':
|
||||||
|
return HEnumValue(self.type, self.name, [value.hash() for value in self.values])
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Struct(Object):
|
||||||
|
type: StructType
|
||||||
|
fields: Dict[str, Object]
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Dictionary(Object):
|
||||||
|
type: DictionaryType
|
||||||
|
dict: 'Dict[Hashable, Object]'
|
||||||
|
|
||||||
|
def get_type(self) -> Type: return self.type
|
||||||
|
|
||||||
|
|
||||||
|
class Hashable(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def __hash__(self) -> int: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_object(self) -> Object: ...
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HInt(Hashable):
|
||||||
|
num: int
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HStr(Hashable):
|
||||||
|
str: str
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(('object', 'str', self.str))
|
||||||
|
|
||||||
|
def get_object(self) -> Object:
|
||||||
|
return Str(self.str)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class HEnumValue(Hashable):
|
||||||
|
type: EnumType
|
||||||
|
name: str
|
||||||
|
values: List_[Hashable]
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(('object', 'enum', self.type, self.name, tuple(self.values)))
|
||||||
|
|
||||||
|
def get_object(self) -> Object:
|
||||||
|
return EnumValue(self.type, self.name, [value.get_object() for value in self.values])
|
425
ppp_parser.ppp
Normal file
425
ppp_parser.ppp
Normal file
@ -0,0 +1,425 @@
|
|||||||
|
import "ppp_tokens.ppp";
|
||||||
|
import "ppp_lexer.ppp";
|
||||||
|
import "ppp_ast.ppp";
|
||||||
|
|
||||||
|
func parse_type_union(lexer: Lexer) -> TypeExpression {
|
||||||
|
union_types: TypeExpression[] = [parse_type(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Pipe))) union_types = union_types + [parse_type(lexer)];
|
||||||
|
if len(union_types) == 1 return union_types[0];
|
||||||
|
return TypeExpression.Union(union_types);
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_type_primary(lexer: Lexer) -> TypeExpression {
|
||||||
|
base_type: TypeExpression;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Open))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) return TypeExpression.Tuple([]);
|
||||||
|
|
||||||
|
types: TypeExpression[] = [parse_type_union(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) types = types + [parse_type_union(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
|
||||||
|
if len(types) == 1 do {
|
||||||
|
match types[0] in {
|
||||||
|
case Union(_) base_type = types[0];
|
||||||
|
case _ base_type = TypeExpression.Tuple(types);
|
||||||
|
}
|
||||||
|
} else base_type = TypeExpression.Tuple(types);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenSquare))) do {
|
||||||
|
assert false, "Unimplemented parse_type_primary array";
|
||||||
|
} else {
|
||||||
|
base_type = TypeExpression.Name(parse_identifier(lexer));
|
||||||
|
}
|
||||||
|
|
||||||
|
while lexer_check_tokens(lexer, [TokenContents.Symbol(Symbol.OpenSquare), TokenContents.Symbol(Symbol.Left)]) do {
|
||||||
|
match lexer_next_token(lexer).contents in {
|
||||||
|
case Symbol(symbol) do {
|
||||||
|
match symbol in {
|
||||||
|
case OpenSquare match lexer_peek_token(lexer).contents in {
|
||||||
|
case Number(number) do {
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_type = TypeExpression.Array(base_type, number);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closing: Symbol;
|
||||||
|
match symbol in {
|
||||||
|
case OpenSquare closing = Symbol.CloseSquare;
|
||||||
|
case Left closing = Symbol.Right;
|
||||||
|
case _ assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
|
||||||
|
match symbol in {
|
||||||
|
case OpenSquare if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(closing))) do {
|
||||||
|
base_type = TypeExpression.List(base_type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generics: TypeExpression[] = [parse_type(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) generics = generics + [parse_type(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(closing));
|
||||||
|
match base_type in {
|
||||||
|
case Specification(_, _) assert false, "Cannot specify an already specified type";
|
||||||
|
}
|
||||||
|
|
||||||
|
base_type = TypeExpression.Specification(base_type, generics);
|
||||||
|
}
|
||||||
|
case _ assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_type(lexer: Lexer) -> TypeExpression {
|
||||||
|
base_type: TypeExpression = parse_type_primary(lexer);
|
||||||
|
if !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Arrow))) return base_type;
|
||||||
|
return_type: TypeExpression = parse_type(lexer);
|
||||||
|
match base_type in {
|
||||||
|
case Tuple(type_expressions) return TypeExpression.Function(type_expressions, return_type);
|
||||||
|
}
|
||||||
|
return TypeExpression.Function([base_type], return_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_type_declaration(lexer: Lexer) -> TypeDeclaration {
|
||||||
|
entry_name: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Colon));
|
||||||
|
entry_type: TypeExpression = parse_type(lexer);
|
||||||
|
return TypeDeclaration{name=entry_name, type_=entry_type};
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_enum_entry(lexer: Lexer) -> EnumEntry {
|
||||||
|
entry_name: str = parse_identifier(lexer);
|
||||||
|
if !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Open))) return EnumEntry{name=entry_name, types=[]};
|
||||||
|
|
||||||
|
entry_types: TypeExpression[] = [parse_type(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) entry_types = entry_types + [parse_type(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
return EnumEntry{name=entry_name, types=entry_types};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_struct_argument(lexer: Lexer) -> (str, Expression) {
|
||||||
|
parameter: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Equal));
|
||||||
|
return (parameter, parse_expression(lexer));
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_dict_entry(lexer: Lexer) -> (Expression, Expression) {
|
||||||
|
key: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Colon));
|
||||||
|
return (key, parse_expression(lexer));
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_primary(lexer: Lexer) -> Expression {
|
||||||
|
base_expression: Expression;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Open))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) base_expression = Expression.Tuple([]);
|
||||||
|
else {
|
||||||
|
elements: Expression[] = [parse_expression(lexer)];
|
||||||
|
singleton: bool = false;
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) do {
|
||||||
|
if lexer_check_token(lexer, TokenContents.Symbol(Symbol.Close)) do {
|
||||||
|
singleton = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
elements = elements + [parse_expression(lexer)];
|
||||||
|
}
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
base_expression = singleton || len(elements) > 1? Expression.Tuple(elements): elements[0];
|
||||||
|
}
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenSquare))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseSquare))) base_expression = Expression.Array([]);
|
||||||
|
else {
|
||||||
|
expressions: Expression[] = [parse_expression(lexer)];
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.For))) do {
|
||||||
|
variable: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Keyword(Keyword.In));
|
||||||
|
expression: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_expression = Expression.LoopComrehension(expressions[0], variable, expression);
|
||||||
|
} else {
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) expressions = expressions + [parse_expression(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_expression = Expression.Array(expressions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenCurly))) do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) base_expression = Expression.Dictionary([]);
|
||||||
|
else {
|
||||||
|
expressions: (Expression, Expression)[] = [parse_dict_entry(lexer)];
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.For))) do {
|
||||||
|
variable: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Keyword(Keyword.In));
|
||||||
|
expression: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
base_expression = Expression.DictComprehension(expressions[0], variable, expression);
|
||||||
|
} else {
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) expressions = expressions + [parse_dict_entry(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
base_expression = Expression.Dictionary(expressions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
token: Token = lexer_next_token(lexer);
|
||||||
|
match token.contents in {
|
||||||
|
case String(string) base_expression = Expression.String(string);
|
||||||
|
case Number(number) base_expression = Expression.Number(number);
|
||||||
|
case Identifier(string) base_expression = Expression.Variable(string);
|
||||||
|
case _ assert false, "Expected identifier, but got %s!" % token_to_str(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while lexer_check_tokens(lexer, [TokenContents.Symbol(Symbol.Open), TokenContents.Symbol(Symbol.OpenSquare), TokenContents.Symbol(Symbol.Dot), TokenContents.Symbol(Symbol.OpenCurly)]) do {
|
||||||
|
match lexer_next_token(lexer).contents in {
|
||||||
|
case Symbol(symbol) match symbol in {
|
||||||
|
case Dot base_expression = Expression.FieldAccess(base_expression, parse_identifier(lexer));
|
||||||
|
case Open do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) base_expression = Expression.FunctionCall(base_expression, []);
|
||||||
|
else {
|
||||||
|
arguments: Expression[] = [parse_expression(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) arguments = arguments + [parse_expression(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
base_expression = Expression.FunctionCall(base_expression, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case OpenSquare do {
|
||||||
|
index: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseSquare));
|
||||||
|
base_expression = Expression.ArrayAccess(base_expression, index);
|
||||||
|
}
|
||||||
|
case OpenCurly do {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) base_expression = Expression.StructInstantiation(base_expression, []);
|
||||||
|
else {
|
||||||
|
struct_arguments: (str, Expression)[] = [parse_struct_argument(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) struct_arguments = struct_arguments + [parse_struct_argument(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
base_expression = Expression.StructInstantiation(base_expression, struct_arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented parse_primary symbol %s" % symbol_to_str(symbol);
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented parse_primary %s" % token_to_str(lexer_next_token(lexer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base_expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_unary(lexer: Lexer) -> Expression {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Tilde))) return Expression.Bnot(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Exclamation))) return Expression.Not(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Plus))) return Expression.UnaryPlus(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Dash))) return Expression.UnaryMinus(parse_unary(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Return))) return Expression.Return(parse_unary(lexer));
|
||||||
|
return parse_primary(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
precedences: dict[Symbol, (Expression, Expression) -> Expression][] = [
|
||||||
|
{Symbol.Dpipe: Expression.Or},
|
||||||
|
{Symbol.Dampersand: Expression.And},
|
||||||
|
{Symbol.Pipe: Expression.Bor},
|
||||||
|
{Symbol.Carot: Expression.Bxor},
|
||||||
|
{Symbol.Ampersand: Expression.Band},
|
||||||
|
{Symbol.Dequal: Expression.Equal, Symbol.NotEqual: Expression.NotEqual},
|
||||||
|
{Symbol.Left: Expression.LessThan, Symbol.Right: Expression.GreaterThan, Symbol.LesserEqual: Expression.LessThanOrEqual, Symbol.GreaterEqual: Expression.GreaterThanOrEqual},
|
||||||
|
{Symbol.Dleft: Expression.ShiftLeft, Symbol.Dright: Expression.ShiftRight},
|
||||||
|
{Symbol.Plus: Expression.Addition, Symbol.Dash: Expression.Subtract},
|
||||||
|
{Symbol.Asterisk: Expression.Multiplication, Symbol.Slash: Expression.Division, Symbol.Percent: Expression.Modulo}
|
||||||
|
];
|
||||||
|
|
||||||
|
func parse_expression_at_level(lexer: Lexer, level: int) -> Expression {
|
||||||
|
if level >= len(precedences) return parse_unary(lexer);
|
||||||
|
left: Expression = parse_expression_at_level(lexer, level+1);
|
||||||
|
tokens: TokenContents[] = [TokenContents.Symbol(symbol) for symbol in precedences[level]];
|
||||||
|
while lexer_check_tokens(lexer, tokens) do {
|
||||||
|
match lexer_next_token(lexer).contents in {
|
||||||
|
case Symbol(symbol) do {
|
||||||
|
expressor: (Expression, Expression) -> Expression = precedences[level][symbol];
|
||||||
|
left = expressor(left, parse_expression_at_level(lexer, level+1));
|
||||||
|
}
|
||||||
|
case _ assert false, "Unreachable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_ternary(lexer: Lexer) -> Expression {
|
||||||
|
expression: Expression = parse_expression_at_level(lexer, 0);
|
||||||
|
if !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.QuestionMark))) return expression;
|
||||||
|
if_true: Expression = parse_expression_at_level(lexer, 0);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Colon));
|
||||||
|
if_false: Expression = parse_ternary(lexer);
|
||||||
|
return Expression.Ternary(expression, if_true, if_false);
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_expression(lexer: Lexer) -> Expression {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Return))) return Expression.Return(parse_expression(lexer));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Lambda))) do {
|
||||||
|
parameters: TypeDeclaration[];
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.EqualArrow))) parameters = [];
|
||||||
|
else do {
|
||||||
|
parameters = [parse_type_declaration(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) parameters = parameters + [parse_type_declaration(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.EqualArrow));
|
||||||
|
}
|
||||||
|
return Expression.Lambda(parameters, parse_expression(lexer));
|
||||||
|
}
|
||||||
|
return parse_ternary(lexer);
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_identifier(lexer: Lexer) -> str {
|
||||||
|
identifier_token: Token = lexer_next_token(lexer);
|
||||||
|
match identifier_token.contents in {
|
||||||
|
case Identifier(identifier) return identifier;
|
||||||
|
case _ assert false, "Expected identifier, but got %s!" % token_to_str(identifier_token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_number(lexer: Lexer) -> int {
|
||||||
|
number_token: Token = lexer_next_token(lexer);
|
||||||
|
match number_token.contents in {
|
||||||
|
case Number(number) return number;
|
||||||
|
case _ assert false, "Expected number!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_string(lexer: Lexer) -> str {
|
||||||
|
string_token: Token = lexer_next_token(lexer);
|
||||||
|
match string_token.contents in {
|
||||||
|
case String(string) return string;
|
||||||
|
case _ assert false, "Expected string!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_valid_target(expression: Expression) -> bool {
|
||||||
|
match expression in {
|
||||||
|
case FieldAccess(subexpression, _) return is_valid_target(subexpression);
|
||||||
|
case Variable(_) return true;
|
||||||
|
case ArrayAccess(array, _) return is_valid_target(array);
|
||||||
|
case _ assert false, "Unimplemented is_valid_target %s" % expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse_statement(lexer: Lexer) -> Statement {
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Enum))) do {
|
||||||
|
enum_name: str = parse_identifier(lexer);
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.OpenCurly));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) return Statement.EnumDefinition(enum_name, []);
|
||||||
|
|
||||||
|
enum_entries: EnumEntry[] = [parse_enum_entry(lexer)];
|
||||||
|
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) enum_entries = enum_entries + [parse_enum_entry(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
return Statement.EnumDefinition(enum_name, enum_entries);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Struct))) do {
|
||||||
|
struct_name: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.OpenCurly));
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) return Statement.StructDefinition(struct_name, []);
|
||||||
|
|
||||||
|
struct_entries: TypeDeclaration[] = [parse_type_declaration(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) struct_entries = struct_entries + [parse_type_declaration(lexer)];
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
return Statement.StructDefinition(struct_name, struct_entries);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Func))) do {
|
||||||
|
function_name: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Open));
|
||||||
|
function_arguments: TypeDeclaration[] = [];
|
||||||
|
if !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Close))) do {
|
||||||
|
function_arguments = function_arguments + [parse_type_declaration(lexer)];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma))) function_arguments = function_arguments + [parse_type_declaration(lexer)];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Close));
|
||||||
|
}
|
||||||
|
|
||||||
|
function_return_type: OptionalTypeExpression = OptionalTypeExpression.None;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Arrow))) function_return_type = OptionalTypeExpression.Some(parse_type(lexer));
|
||||||
|
|
||||||
|
function_body: Statement = parse_statement(lexer);
|
||||||
|
return Statement.FunctionDefinition(function_name, function_arguments, function_return_type, function_body);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.If))) do {
|
||||||
|
return Statement.If(parse_expression(lexer), parse_statement(lexer), is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Else)))? OptionalStatement.Some(parse_statement(lexer)): OptionalStatement.None);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Match))) do {
|
||||||
|
value: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Keyword(Keyword.In));
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.OpenCurly));
|
||||||
|
|
||||||
|
cases: (Expression, Statement)[] = [];
|
||||||
|
while is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Case))) cases = cases + [(parse_expression(lexer), parse_statement(lexer))];
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.CloseCurly));
|
||||||
|
return Statement.Match(value, cases);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Assert))) do {
|
||||||
|
condition: Expression = parse_expression(lexer);
|
||||||
|
message: OptionalExpression = is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Comma)))? OptionalExpression.Some(parse_expression(lexer)): OptionalExpression.None;
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Assert(condition, message);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Do))) do {
|
||||||
|
body: Statement = parse_statement(lexer);
|
||||||
|
condition: OptionalExpression = OptionalExpression.None;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.While))) do {
|
||||||
|
condition = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
}
|
||||||
|
return Statement.DoWhile(body, condition);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.While))) do {
|
||||||
|
return Statement.While(parse_expression(lexer), parse_statement(lexer));
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.For))) do {
|
||||||
|
variable: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Keyword(Keyword.In));
|
||||||
|
expression: Expression = parse_expression(lexer);
|
||||||
|
body: Statement = parse_statement(lexer);
|
||||||
|
return Statement.ForLoop(variable, expression, body);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Continue))) do {
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Continue;
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Break))) do {
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Break;
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Import))) do {
|
||||||
|
file: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Import(file);
|
||||||
|
} else if is_some_token(lexer_take_token(lexer, TokenContents.Keyword(Keyword.Type))) do {
|
||||||
|
name: str = parse_identifier(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Equal));
|
||||||
|
type_expression: TypeExpression = parse_type(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.TypeDefinition(name, type_expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.OpenCurly))) do {
|
||||||
|
statements: Statement[] = [];
|
||||||
|
while !is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.CloseCurly))) statements = statements + [parse_statement(lexer)];
|
||||||
|
return Statement.Statements(statements);
|
||||||
|
} else {
|
||||||
|
expression: Expression = parse_expression(lexer);
|
||||||
|
type_: OptionalTypeExpression = OptionalTypeExpression.None;
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Colon))) do {
|
||||||
|
match expression in {
|
||||||
|
case Variable(_) type_ = OptionalTypeExpression.Some(parse_type(lexer));
|
||||||
|
case _ assert false, "Invalid target";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_some_token(lexer_take_token(lexer, TokenContents.Symbol(Symbol.Equal))) do {
|
||||||
|
assert is_valid_target(expression), "Invalid target!";
|
||||||
|
right_expression: Expression = parse_expression(lexer);
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
return Statement.Assignment(expression, right_expression, type_);
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer_assert_token(lexer, TokenContents.Symbol(Symbol.Semicolon));
|
||||||
|
match expression in {
|
||||||
|
case Variable(name) match type_ in {
|
||||||
|
case Some(type_expression) return Statement.TypeDeclaration(TypeDeclaration{name=name, type_=type_expression});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Statement.Expression(expression);
|
||||||
|
}
|
||||||
|
}
|
383
ppp_parser.py
Normal file
383
ppp_parser.py
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
from typing import Callable, Dict, List, Optional, Tuple
|
||||||
|
from ppp_lexer import Lexer
|
||||||
|
from ppp_tokens import IdentifierToken, Keyword, KeywordToken, NumberToken, StringToken, Symbol, SymbolToken
|
||||||
|
from ppp_ast import *
|
||||||
|
|
||||||
|
def parse_identifier(lexer: Lexer) -> str:
|
||||||
|
identifier = lexer.assert_tokenkind(IdentifierToken)
|
||||||
|
assert isinstance(identifier.contents, IdentifierToken)
|
||||||
|
return identifier.contents.identifier
|
||||||
|
|
||||||
|
def parse_number(lexer: Lexer) -> int:
|
||||||
|
number = lexer.assert_tokenkind(NumberToken)
|
||||||
|
assert isinstance(number.contents, NumberToken)
|
||||||
|
return number.contents.number
|
||||||
|
|
||||||
|
def parse_string(lexer: Lexer) -> str:
|
||||||
|
string = lexer.assert_tokenkind(StringToken)
|
||||||
|
assert isinstance(string.contents, StringToken)
|
||||||
|
return string.contents.string
|
||||||
|
|
||||||
|
def parse_type_primary(lexer: Lexer) -> TypeExpression:
|
||||||
|
base_type: TypeExpression
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Open)):
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Close)): return TupleTypeExpr([])
|
||||||
|
|
||||||
|
def parse_union(lexer: Lexer) -> TypeExpression:
|
||||||
|
union_types: List[TypeExpression] = [parse_type(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Pipe)):
|
||||||
|
union_types.append(parse_type(lexer))
|
||||||
|
if len(union_types) == 1:
|
||||||
|
return union_types[0]
|
||||||
|
return UnionTypeExpr(union_types)
|
||||||
|
|
||||||
|
types: List[TypeExpression] = [parse_union(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
types.append(parse_union(lexer))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Close))
|
||||||
|
if len(types) == 1 and isinstance(types[0], UnionTypeExpr):
|
||||||
|
base_type = types[0]
|
||||||
|
else:
|
||||||
|
base_type = TupleTypeExpr(types)
|
||||||
|
elif lexer.take_token(SymbolToken(Symbol.OpenSquare)):
|
||||||
|
type = parse_type(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseSquare))
|
||||||
|
base_type = ListTypeExpr(type)
|
||||||
|
else:
|
||||||
|
name = parse_identifier(lexer)
|
||||||
|
base_type = TypeName(name)
|
||||||
|
|
||||||
|
while (opening_token := lexer.take_tokens(SymbolToken(Symbol.OpenSquare), SymbolToken(Symbol.Left))):
|
||||||
|
assert isinstance(opening_token.contents, SymbolToken)
|
||||||
|
opening = opening_token.contents.symbol
|
||||||
|
|
||||||
|
if opening == Symbol.OpenSquare and lexer.check_tokenkind(NumberToken):
|
||||||
|
number = parse_number(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseSquare))
|
||||||
|
base_type = ArrayTypeExpr(base_type, number)
|
||||||
|
continue
|
||||||
|
|
||||||
|
opening2closing_map: Dict[Symbol, Symbol] = {
|
||||||
|
Symbol.OpenSquare: Symbol.CloseSquare,
|
||||||
|
Symbol.Left: Symbol.Right
|
||||||
|
}
|
||||||
|
assert opening in opening2closing_map, "Unreachable"
|
||||||
|
closing = opening2closing_map[opening]
|
||||||
|
|
||||||
|
if opening == Symbol.OpenSquare and lexer.take_token(SymbolToken(closing)):
|
||||||
|
base_type = ListTypeExpr(base_type)
|
||||||
|
continue
|
||||||
|
|
||||||
|
generics: List[TypeExpression] = [parse_type(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)): generics.append(parse_type(lexer))
|
||||||
|
|
||||||
|
lexer.assert_token(SymbolToken(closing))
|
||||||
|
assert not isinstance(base_type, TypeSpecification)
|
||||||
|
base_type = TypeSpecification(base_type, generics)
|
||||||
|
|
||||||
|
return base_type
|
||||||
|
|
||||||
|
def parse_type(lexer: Lexer) -> TypeExpression:
|
||||||
|
base_type = parse_type_primary(lexer)
|
||||||
|
if not lexer.take_token(SymbolToken(Symbol.Arrow)): return base_type
|
||||||
|
return_type = parse_type(lexer)
|
||||||
|
return FunctionTypeExpr([base_type] if not isinstance(base_type, TupleTypeExpr) else base_type.types, return_type)
|
||||||
|
|
||||||
|
def parse_type_declaration(lexer: Lexer) -> TypeDeclaration:
|
||||||
|
entry_name = parse_identifier(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Colon))
|
||||||
|
entry_type = parse_type(lexer)
|
||||||
|
return TypeDeclaration(entry_name, entry_type)
|
||||||
|
|
||||||
|
def parse_enum_entry(lexer: Lexer) -> EnumEntry:
|
||||||
|
entry_name = parse_identifier(lexer)
|
||||||
|
if not lexer.take_token(SymbolToken(Symbol.Open)):
|
||||||
|
return EnumEntry(entry_name, [])
|
||||||
|
|
||||||
|
entry_types: List[TypeExpression] = [parse_type(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
entry_types.append(parse_type(lexer))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Close))
|
||||||
|
return EnumEntry(entry_name, entry_types)
|
||||||
|
|
||||||
|
def parse_primary(lexer: Lexer) -> Expression:
|
||||||
|
base_expression: Expression
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Open)):
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Close)):
|
||||||
|
base_expression = TupleExpr([])
|
||||||
|
else:
|
||||||
|
elements: List[Expression] = [parse_expression(lexer)]
|
||||||
|
singleton: bool = False
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
if lexer.check_token(SymbolToken(Symbol.Close)) and len(elements) == 1:
|
||||||
|
singleton = True
|
||||||
|
break
|
||||||
|
elements.append(parse_expression(lexer))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Close))
|
||||||
|
if singleton or len(elements) > 1:
|
||||||
|
base_expression = TupleExpr(elements)
|
||||||
|
else:
|
||||||
|
base_expression = elements[0]
|
||||||
|
elif lexer.take_token(SymbolToken(Symbol.OpenSquare)):
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.CloseSquare)):
|
||||||
|
base_expression = Array([])
|
||||||
|
else:
|
||||||
|
expressions: List[Expression] = [parse_expression(lexer)]
|
||||||
|
if lexer.take_token(KeywordToken(Keyword.For)):
|
||||||
|
variable = parse_identifier(lexer) # TODO: Pattern matching
|
||||||
|
lexer.assert_token(KeywordToken(Keyword.In))
|
||||||
|
expression = parse_expression(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseSquare))
|
||||||
|
base_expression = LoopComprehension(expressions[0], variable, expression)
|
||||||
|
else:
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
expressions.append(parse_expression(lexer))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseSquare))
|
||||||
|
base_expression = Array(expressions)
|
||||||
|
elif lexer.take_token(SymbolToken(Symbol.OpenCurly)):
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.CloseCurly)):
|
||||||
|
base_expression = DictionaryExpr([])
|
||||||
|
else:
|
||||||
|
def parse_dict_entry() -> Tuple[Expression, Expression]:
|
||||||
|
key = parse_expression(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Colon))
|
||||||
|
return (key, parse_expression(lexer))
|
||||||
|
dict_entries: List[Tuple[Expression, Expression]] = [parse_dict_entry()]
|
||||||
|
if lexer.take_token(KeywordToken(Keyword.For)):
|
||||||
|
variable = parse_identifier(lexer) # TODO: Pattern matching
|
||||||
|
lexer.assert_token(KeywordToken(Keyword.In))
|
||||||
|
expression = parse_expression(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseCurly))
|
||||||
|
base_expression = DictComprehension(dict_entries[0], variable, expression)
|
||||||
|
else:
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
dict_entries.append(parse_dict_entry())
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseCurly))
|
||||||
|
base_expression = DictionaryExpr(dict_entries)
|
||||||
|
elif lexer.check_tokenkind(StringToken):
|
||||||
|
base_expression = String(parse_string(lexer))
|
||||||
|
elif lexer.check_tokenkind(NumberToken):
|
||||||
|
base_expression = Number(parse_number(lexer))
|
||||||
|
else:
|
||||||
|
base_expression = Variable(parse_identifier(lexer))
|
||||||
|
|
||||||
|
while (token := lexer.take_tokens(SymbolToken(Symbol.Open), SymbolToken(Symbol.OpenSquare), SymbolToken(Symbol.Dot), SymbolToken(Symbol.OpenCurly))):
|
||||||
|
match token.contents:
|
||||||
|
case SymbolToken(symbol):
|
||||||
|
match symbol:
|
||||||
|
case Symbol.Dot:
|
||||||
|
field = parse_identifier(lexer)
|
||||||
|
base_expression = FieldAccess(base_expression, field)
|
||||||
|
case Symbol.Open:
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Close)):
|
||||||
|
base_expression = FunctionCall(base_expression, [])
|
||||||
|
else:
|
||||||
|
arguments: List[Expression] = [parse_expression(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
arguments.append(parse_expression(lexer))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Close))
|
||||||
|
base_expression = FunctionCall(base_expression, arguments)
|
||||||
|
case Symbol.OpenSquare:
|
||||||
|
index = parse_expression(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseSquare))
|
||||||
|
base_expression = ArrayAccess(base_expression, index)
|
||||||
|
case Symbol.OpenCurly:
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.CloseCurly)):
|
||||||
|
base_expression = StructInstantiation(base_expression, [])
|
||||||
|
else:
|
||||||
|
def parse_argument() -> Tuple[str, Expression]:
|
||||||
|
parameter = parse_identifier(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Equal))
|
||||||
|
return (parameter, parse_expression(lexer))
|
||||||
|
|
||||||
|
struct_arguments: List[Tuple[str, Expression]] = [parse_argument()]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)): struct_arguments.append(parse_argument())
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseCurly))
|
||||||
|
base_expression = StructInstantiation(base_expression, struct_arguments)
|
||||||
|
case _: assert False, ("Unimplemented", symbol)
|
||||||
|
case _: assert False, ("Unimplemented", token)
|
||||||
|
|
||||||
|
return base_expression
|
||||||
|
|
||||||
|
def parse_unary(lexer: Lexer) -> Expression:
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Tilde)): return Bnot(parse_unary(lexer))
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Exclamation)): return Not(parse_unary(lexer))
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Plus)): return UnaryPlus(parse_unary(lexer))
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Dash)): return UnaryMinus(parse_unary(lexer))
|
||||||
|
if lexer.take_token(KeywordToken(Keyword.Return)): return Return(parse_unary(lexer))
|
||||||
|
return parse_primary(lexer)
|
||||||
|
|
||||||
|
Precedence = Dict[Symbol, Callable[[Expression, Expression], Expression]]
|
||||||
|
|
||||||
|
precedences: List[Precedence] = [
|
||||||
|
{Symbol.Dpipe: Or},
|
||||||
|
{Symbol.Dampersand: And},
|
||||||
|
{Symbol.Pipe: Bor},
|
||||||
|
{Symbol.Carot: Bxor},
|
||||||
|
{Symbol.Ampersand: Band},
|
||||||
|
{Symbol.Dequal: Equal, Symbol.NotEqual: NotEqual},
|
||||||
|
{Symbol.Left: LessThan, Symbol.Right: GreaterThan, Symbol.LesserEqual: LessThanOrEqual, Symbol.GreaterEqual: GreaterThanOrEqual},
|
||||||
|
{Symbol.Dleft: ShiftLeft, Symbol.Dright: ShiftRight},
|
||||||
|
{Symbol.Plus: Addition, Symbol.Dash: Subtract},
|
||||||
|
{Symbol.Asterisk: Multiplication, Symbol.Slash: Division, Symbol.Percent: Modulo}
|
||||||
|
]
|
||||||
|
|
||||||
|
def parse_expression_at_level(lexer: Lexer, level: int=0) -> Expression:
|
||||||
|
if level >= len(precedences): return parse_unary(lexer)
|
||||||
|
left = parse_expression_at_level(lexer, level+1)
|
||||||
|
tokens = [SymbolToken(symbol) for symbol in precedences[level]]
|
||||||
|
while (token := lexer.take_tokens(*tokens)):
|
||||||
|
assert isinstance(token.contents, SymbolToken)
|
||||||
|
left = precedences[level][token.contents.symbol](left, parse_expression_at_level(lexer, level+1))
|
||||||
|
return left
|
||||||
|
|
||||||
|
def parse_ternary(lexer: Lexer) -> Expression:
|
||||||
|
expression = parse_expression_at_level(lexer)
|
||||||
|
if not lexer.take_token(SymbolToken(Symbol.QuestionMark)): return expression
|
||||||
|
if_true = parse_expression_at_level(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Colon))
|
||||||
|
if_false = parse_ternary(lexer)
|
||||||
|
return Ternary(expression, if_true, if_false)
|
||||||
|
|
||||||
|
def parse_expression(lexer: Lexer) -> Expression:
|
||||||
|
if lexer.take_token(KeywordToken(Keyword.Return)): return Return(parse_expression(lexer))
|
||||||
|
if lexer.take_token(KeywordToken(Keyword.Lambda)):
|
||||||
|
parameters: List[TypeDeclaration]
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.EqualArrow)):
|
||||||
|
parameters = []
|
||||||
|
else:
|
||||||
|
parameters = [parse_type_declaration(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
parameters.append(parse_type_declaration(lexer))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.EqualArrow))
|
||||||
|
return Lambda(parameters, parse_expression(lexer))
|
||||||
|
return parse_ternary(lexer)
|
||||||
|
|
||||||
|
def is_valid_target(expression: Expression) -> bool:
|
||||||
|
match expression:
|
||||||
|
case FieldAccess(subexpression, _): return is_valid_target(subexpression)
|
||||||
|
case Variable(_): return True
|
||||||
|
case ArrayAccess(array, _): return is_valid_target(array)
|
||||||
|
case _: assert False, ("Unimplemeneted", expression)
|
||||||
|
assert False, "Unreachable"
|
||||||
|
|
||||||
|
def parse_statement(lexer: Lexer) -> Statement:
|
||||||
|
if lexer.take_token(KeywordToken(Keyword.Enum)):
|
||||||
|
enum_name = parse_identifier(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.OpenCurly))
|
||||||
|
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.CloseCurly)): return EnumDefinition(enum_name, [])
|
||||||
|
|
||||||
|
enum_entries: List[EnumEntry] = [parse_enum_entry(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
enum_entries.append(parse_enum_entry(lexer))
|
||||||
|
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseCurly))
|
||||||
|
return EnumDefinition(enum_name, enum_entries)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Struct)):
|
||||||
|
struct_name = parse_identifier(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.OpenCurly))
|
||||||
|
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.CloseCurly)): return StructDefinition(struct_name, [])
|
||||||
|
|
||||||
|
struct_entries: List[TypeDeclaration] = [parse_type_declaration(lexer)]
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
struct_entries.append(parse_type_declaration(lexer))
|
||||||
|
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseCurly))
|
||||||
|
return StructDefinition(struct_name, struct_entries)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Func)):
|
||||||
|
function_name = parse_identifier(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Open))
|
||||||
|
function_arguments: List[TypeDeclaration] = []
|
||||||
|
if not lexer.take_token(SymbolToken(Symbol.Close)):
|
||||||
|
function_arguments.append(parse_type_declaration(lexer))
|
||||||
|
while lexer.take_token(SymbolToken(Symbol.Comma)):
|
||||||
|
function_arguments.append(parse_type_declaration(lexer))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Close))
|
||||||
|
function_return_type: Optional[TypeExpression] = None
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Arrow)):
|
||||||
|
function_return_type = parse_type(lexer)
|
||||||
|
|
||||||
|
function_body = parse_statement(lexer)
|
||||||
|
|
||||||
|
return FunctionDefinition(function_name, function_arguments, function_return_type, function_body)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.If)):
|
||||||
|
return IfStatement(
|
||||||
|
parse_expression(lexer),
|
||||||
|
parse_statement(lexer),
|
||||||
|
parse_statement(lexer) if lexer.take_token(KeywordToken(Keyword.Else)) else None
|
||||||
|
)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Else)):
|
||||||
|
assert False, "Unmatched else"
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.While)):
|
||||||
|
return WhileStatement(
|
||||||
|
parse_expression(lexer),
|
||||||
|
parse_statement(lexer)
|
||||||
|
)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Break)):
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
return BreakStatement()
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Continue)):
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
return ContinueStatement()
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Do)):
|
||||||
|
body = parse_statement(lexer)
|
||||||
|
condition: Optional[Expression] = None
|
||||||
|
if lexer.take_token(KeywordToken(Keyword.While)):
|
||||||
|
condition = parse_expression(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
return DoWhileStatement(body, condition)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Match)):
|
||||||
|
value = parse_expression(lexer)
|
||||||
|
lexer.assert_token(KeywordToken(Keyword.In)) # to prevent it from parsing it as a struct instantiation
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.OpenCurly))
|
||||||
|
|
||||||
|
cases: List[Tuple[Expression, Statement]] = []
|
||||||
|
while lexer.take_token(KeywordToken(Keyword.Case)): cases.append((parse_expression(lexer), parse_statement(lexer)))
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.CloseCurly))
|
||||||
|
return MatchStatement(value, cases)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Assert)):
|
||||||
|
condition = parse_expression(lexer)
|
||||||
|
message = parse_expression(lexer) if lexer.take_token(SymbolToken(Symbol.Comma)) else None
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
return AssertStatement(condition, message)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.For)):
|
||||||
|
variable = parse_identifier(lexer) # TODO: Allow for pattern matching here
|
||||||
|
lexer.assert_token(KeywordToken(Keyword.In))
|
||||||
|
expression = parse_expression(lexer)
|
||||||
|
body = parse_statement(lexer)
|
||||||
|
return ForLoop(variable, expression, body)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Import)):
|
||||||
|
file = parse_expression(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
return Import(file)
|
||||||
|
elif lexer.take_token(KeywordToken(Keyword.Type)):
|
||||||
|
name = parse_identifier(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Equal))
|
||||||
|
type_expression = parse_type(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
return TypeDefinition(name, type_expression)
|
||||||
|
elif lexer.check_tokenkind(KeywordToken) and not lexer.check_tokens(KeywordToken(Keyword.Return), KeywordToken(Keyword.Lambda)):
|
||||||
|
assert False, ("Unimplemented", lexer.next_token(), lexer.next_token(), lexer.next_token())
|
||||||
|
elif lexer.take_token(SymbolToken(Symbol.OpenCurly)):
|
||||||
|
statements: List[Statement] = []
|
||||||
|
while not lexer.take_token(SymbolToken(Symbol.CloseCurly)):
|
||||||
|
statements.append(parse_statement(lexer))
|
||||||
|
return Statements(statements)
|
||||||
|
else:
|
||||||
|
expression = parse_expression(lexer)
|
||||||
|
type: Optional[TypeExpression] = None
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Colon)):
|
||||||
|
assert isinstance(expression, Variable), "Cannot declare types for anything besides a variable"
|
||||||
|
type = parse_type(lexer)
|
||||||
|
|
||||||
|
if lexer.take_token(SymbolToken(Symbol.Equal)):
|
||||||
|
assert is_valid_target(expression), ("Invalid target!", expression)
|
||||||
|
right_expression = parse_expression(lexer)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
return Assignment(expression, right_expression, type)
|
||||||
|
lexer.assert_token(SymbolToken(Symbol.Semicolon))
|
||||||
|
if type and isinstance(expression, Variable):
|
||||||
|
return TypeDeclarationStatement(TypeDeclaration(expression.name, type))
|
||||||
|
return ExpressionStatement(expression)
|
7
ppp_stdlib.ppp
Normal file
7
ppp_stdlib.ppp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import "ppp_object.ppp";
|
||||||
|
import "ppp_types.ppp";
|
||||||
|
|
||||||
|
variables: dict[str, Object] = {
|
||||||
|
"str": Object.Type(Type.Str),
|
||||||
|
"bool": Object.Type(Type.Bool)
|
||||||
|
};
|
110
ppp_stdlib.py
Normal file
110
ppp_stdlib.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
from typing import Callable, Dict, List, Tuple
|
||||||
|
|
||||||
|
from ppp_ast import Statements
|
||||||
|
from ppp_object import Bool, EnumValue, Int, Object, Function, Str, TypeObject, Void, List as ListObject
|
||||||
|
from ppp_types import Bool as BoolType, DictionaryType, FunctionType, GenericType, Int as IntType, Str as StrType, Type, TypeType, VariableType, Void as VoidType, Object as ObjectType, UnionType, ListType
|
||||||
|
|
||||||
|
|
||||||
|
def PythonFunction(name: str, parameters: List[Tuple[str, Type]], return_type: Type, func: Callable[..., Object]) -> Object:
|
||||||
|
return Function(FunctionType([parameter[1] for parameter in parameters], return_type), (name, parameters, return_type, Statements([]), lambda _0, _1, _2, _3, *args: func(*args)))
|
||||||
|
|
||||||
|
def print_impl(str_: Object) -> Object:
|
||||||
|
assert isinstance(str_, Str)
|
||||||
|
print(str_.str, end='')
|
||||||
|
return Void
|
||||||
|
|
||||||
|
Print = PythonFunction("print", [('string', StrType)], VoidType, print_impl)
|
||||||
|
|
||||||
|
def int_to_str_impl(int_: Object) -> Object:
|
||||||
|
assert isinstance(int_, Int)
|
||||||
|
return Str(str(int_.num))
|
||||||
|
|
||||||
|
IntToStr = PythonFunction("int_to_str", [('integer', IntType)], StrType, int_to_str_impl)
|
||||||
|
|
||||||
|
def debug_print_impl(obj: Object) -> Object:
|
||||||
|
print(obj)
|
||||||
|
return Void
|
||||||
|
|
||||||
|
DebugPrint = PythonFunction("debug_print", [('object', ObjectType)], VoidType, debug_print_impl)
|
||||||
|
|
||||||
|
def read_impl(str_: Object) -> Object:
|
||||||
|
assert isinstance(str_, Str)
|
||||||
|
with open(str_.str) as f: return Str(f.read())
|
||||||
|
|
||||||
|
Read = PythonFunction("read", [('file_path', StrType)], StrType, read_impl)
|
||||||
|
|
||||||
|
def len_impl(list_: Object) -> Object:
|
||||||
|
assert list_.get_type().is_indexable(), list_
|
||||||
|
match list_:
|
||||||
|
case Str(str): return Int(len(str))
|
||||||
|
case ListObject(_, list): return Int(len(list))
|
||||||
|
case _: assert False, ("Unimplemented", list_)
|
||||||
|
assert False
|
||||||
|
|
||||||
|
Len = PythonFunction("len", [('list', UnionType([ListType(VariableType("")), StrType]))], IntType, len_impl)
|
||||||
|
|
||||||
|
def str_to_int_impl(str_: Object) -> Object:
|
||||||
|
assert isinstance(str_, Str)
|
||||||
|
assert str_.str.isdigit()
|
||||||
|
return Int(int(str_.str))
|
||||||
|
|
||||||
|
StrToInt = PythonFunction("str_to_int", [('string', StrType)], IntType, str_to_int_impl)
|
||||||
|
|
||||||
|
def range_impl(start: Object, end: Object) -> Object:
|
||||||
|
assert isinstance(start, Int)
|
||||||
|
assert isinstance(end, Int)
|
||||||
|
return ListObject(ListType(IntType), [Int(i) for i in range(start.num, end.num)])
|
||||||
|
|
||||||
|
Range = PythonFunction("range", [('start', IntType), ('end', IntType)], ListType(IntType), range_impl)
|
||||||
|
|
||||||
|
def join_by_impl(seperator: Object, list: Object) -> Object:
|
||||||
|
assert isinstance(seperator, Str)
|
||||||
|
assert isinstance(list, ListObject)
|
||||||
|
if len(list.list) == 0: return Str("")
|
||||||
|
assert list.type.type.is_subtype_of(StrType), list
|
||||||
|
new_array: List[str] = []
|
||||||
|
for str_ in list.list:
|
||||||
|
assert isinstance(str_, Str)
|
||||||
|
new_array.append(str_.str)
|
||||||
|
return Str(seperator.str.join(new_array))
|
||||||
|
|
||||||
|
JoinBy = PythonFunction("join_by", [('seperator', StrType), ('list', ListType(StrType))], StrType, join_by_impl)
|
||||||
|
|
||||||
|
def id_impl(obj: Object) -> Object:
|
||||||
|
match obj:
|
||||||
|
case EnumValue(_, _, _): return Int(id(obj))
|
||||||
|
case _: assert False, ("Unimplemented", obj)
|
||||||
|
|
||||||
|
|
||||||
|
Id = PythonFunction("id", [('object', ObjectType)], IntType, id_impl)
|
||||||
|
|
||||||
|
StrTypeObj = TypeObject(StrType)
|
||||||
|
IntTypeObj = TypeObject(IntType)
|
||||||
|
VoidTypeObj = TypeObject(VoidType)
|
||||||
|
BoolTypeObj = TypeObject(BoolType)
|
||||||
|
DictTypeObj = TypeObject(GenericType([VariableType("K"), VariableType("V")], DictionaryType(VariableType("K"), VariableType("V"))))
|
||||||
|
|
||||||
|
True_ = Bool(True)
|
||||||
|
False_ = Bool(False)
|
||||||
|
|
||||||
|
NoneObj = Void
|
||||||
|
|
||||||
|
variables: Dict[str, Object] = {
|
||||||
|
'print': Print,
|
||||||
|
'true': True_,
|
||||||
|
'false': False_,
|
||||||
|
'int_to_str': IntToStr,
|
||||||
|
'str': StrTypeObj,
|
||||||
|
'int': IntTypeObj,
|
||||||
|
'bool': BoolTypeObj,
|
||||||
|
'void': VoidTypeObj,
|
||||||
|
'dict': DictTypeObj,
|
||||||
|
'debug_print': DebugPrint,
|
||||||
|
'read': Read,
|
||||||
|
'len': Len,
|
||||||
|
'str_to_int': StrToInt,
|
||||||
|
'none': NoneObj,
|
||||||
|
'range': Range,
|
||||||
|
'join_by': JoinBy,
|
||||||
|
'id': Id
|
||||||
|
}
|
237
ppp_tokens.ppp
Normal file
237
ppp_tokens.ppp
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
enum Keyword {
|
||||||
|
Enum,
|
||||||
|
Struct,
|
||||||
|
Func,
|
||||||
|
If,
|
||||||
|
Else,
|
||||||
|
While,
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Do,
|
||||||
|
For,
|
||||||
|
To,
|
||||||
|
In,
|
||||||
|
Match,
|
||||||
|
Case,
|
||||||
|
Assert,
|
||||||
|
Return,
|
||||||
|
Lambda,
|
||||||
|
Import,
|
||||||
|
Type
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalKeyword {
|
||||||
|
Some(Keyword),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyword_from_str(keyword: str) -> OptionalKeyword {
|
||||||
|
if keyword == "enum" return OptionalKeyword.Some(Keyword.Enum);
|
||||||
|
if keyword == "struct" return OptionalKeyword.Some(Keyword.Struct);
|
||||||
|
if keyword == "func" return OptionalKeyword.Some(Keyword.Func);
|
||||||
|
if keyword == "if" return OptionalKeyword.Some(Keyword.If);
|
||||||
|
if keyword == "else" return OptionalKeyword.Some(Keyword.Else);
|
||||||
|
if keyword == "while" return OptionalKeyword.Some(Keyword.While);
|
||||||
|
if keyword == "break" return OptionalKeyword.Some(Keyword.Break);
|
||||||
|
if keyword == "continue" return OptionalKeyword.Some(Keyword.Continue);
|
||||||
|
if keyword == "do" return OptionalKeyword.Some(Keyword.Do);
|
||||||
|
if keyword == "for" return OptionalKeyword.Some(Keyword.For);
|
||||||
|
if keyword == "to" return OptionalKeyword.Some(Keyword.To);
|
||||||
|
if keyword == "in" return OptionalKeyword.Some(Keyword.In);
|
||||||
|
if keyword == "match" return OptionalKeyword.Some(Keyword.Match);
|
||||||
|
if keyword == "case" return OptionalKeyword.Some(Keyword.Case);
|
||||||
|
if keyword == "assert" return OptionalKeyword.Some(Keyword.Assert);
|
||||||
|
if keyword == "return" return OptionalKeyword.Some(Keyword.Return);
|
||||||
|
if keyword == "lambda" return OptionalKeyword.Some(Keyword.Lambda);
|
||||||
|
if keyword == "import" return OptionalKeyword.Some(Keyword.Import);
|
||||||
|
if keyword == "type" return OptionalKeyword.Some(Keyword.Type);
|
||||||
|
return OptionalKeyword.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyword_to_str(keyword: Keyword) -> str {
|
||||||
|
match keyword in {
|
||||||
|
case Enum return "enum";
|
||||||
|
case Struct return "struct";
|
||||||
|
case Func return "func";
|
||||||
|
case If return "if";
|
||||||
|
case Else return "else";
|
||||||
|
case While return "while";
|
||||||
|
case Break return "break";
|
||||||
|
case Continue return "continue";
|
||||||
|
case Do return "do";
|
||||||
|
case For return "for";
|
||||||
|
case To return "to";
|
||||||
|
case In return "in";
|
||||||
|
case Match return "match";
|
||||||
|
case Case return "case";
|
||||||
|
case Assert return "assert";
|
||||||
|
case Return return "return";
|
||||||
|
case Lambda return "lambda";
|
||||||
|
case Import return "import";
|
||||||
|
case Type return "type";
|
||||||
|
}
|
||||||
|
assert false, "Invalid keyword";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Symbol {
|
||||||
|
Open,
|
||||||
|
Close,
|
||||||
|
OpenCurly,
|
||||||
|
CloseCurly,
|
||||||
|
Comma,
|
||||||
|
OpenSquare,
|
||||||
|
CloseSquare,
|
||||||
|
Colon,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Arrow,
|
||||||
|
Semicolon,
|
||||||
|
Equal,
|
||||||
|
Dequal,
|
||||||
|
Exclamation,
|
||||||
|
NotEqual,
|
||||||
|
Dot,
|
||||||
|
Plus,
|
||||||
|
Dash,
|
||||||
|
Asterisk,
|
||||||
|
Dasterisk,
|
||||||
|
Slash,
|
||||||
|
QuestionMark,
|
||||||
|
Ampersand,
|
||||||
|
Dampersand,
|
||||||
|
Pipe,
|
||||||
|
Dpipe,
|
||||||
|
Dleft,
|
||||||
|
Dright,
|
||||||
|
GreaterEqual,
|
||||||
|
LesserEqual,
|
||||||
|
Percent,
|
||||||
|
Tilde,
|
||||||
|
Carot
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalSymbol {
|
||||||
|
Some(Symbol),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func symbol_from_str(symbol: str) -> OptionalSymbol {
|
||||||
|
if symbol == "(" return OptionalSymbol.Some(Symbol.Open);
|
||||||
|
if symbol == ")" return OptionalSymbol.Some(Symbol.Close);
|
||||||
|
if symbol == "{" return OptionalSymbol.Some(Symbol.OpenCurly);
|
||||||
|
if symbol == "}" return OptionalSymbol.Some(Symbol.CloseCurly);
|
||||||
|
if symbol == "," return OptionalSymbol.Some(Symbol.Comma);
|
||||||
|
if symbol == "[" return OptionalSymbol.Some(Symbol.OpenSquare);
|
||||||
|
if symbol == "]" return OptionalSymbol.Some(Symbol.CloseSquare);
|
||||||
|
if symbol == ":" return OptionalSymbol.Some(Symbol.Colon);
|
||||||
|
if symbol == "<" return OptionalSymbol.Some(Symbol.Left);
|
||||||
|
if symbol == ">" return OptionalSymbol.Some(Symbol.Right);
|
||||||
|
if symbol == "->" return OptionalSymbol.Some(Symbol.Arrow);
|
||||||
|
if symbol == ";" return OptionalSymbol.Some(Symbol.Semicolon);
|
||||||
|
if symbol == "=" return OptionalSymbol.Some(Symbol.Equal);
|
||||||
|
if symbol == "==" return OptionalSymbol.Some(Symbol.Dequal);
|
||||||
|
if symbol == "!" return OptionalSymbol.Some(Symbol.Exclamation);
|
||||||
|
if symbol == "!=" return OptionalSymbol.Some(Symbol.NotEqual);
|
||||||
|
if symbol == "." return OptionalSymbol.Some(Symbol.Dot);
|
||||||
|
if symbol == "+" return OptionalSymbol.Some(Symbol.Plus);
|
||||||
|
if symbol == "-" return OptionalSymbol.Some(Symbol.Dash);
|
||||||
|
if symbol == "*" return OptionalSymbol.Some(Symbol.Asterisk);
|
||||||
|
if symbol == "**" return OptionalSymbol.Some(Symbol.Dasterisk);
|
||||||
|
if symbol == "/" return OptionalSymbol.Some(Symbol.Slash);
|
||||||
|
if symbol == "?" return OptionalSymbol.Some(Symbol.QuestionMark);
|
||||||
|
if symbol == "&" return OptionalSymbol.Some(Symbol.Ampersand);
|
||||||
|
if symbol == "&&" return OptionalSymbol.Some(Symbol.Dampersand);
|
||||||
|
if symbol == "|" return OptionalSymbol.Some(Symbol.Pipe);
|
||||||
|
if symbol == "||" return OptionalSymbol.Some(Symbol.Dpipe);
|
||||||
|
if symbol == "<<" return OptionalSymbol.Some(Symbol.Dleft);
|
||||||
|
if symbol == ">>" return OptionalSymbol.Some(Symbol.Dright);
|
||||||
|
if symbol == ">=" return OptionalSymbol.Some(Symbol.GreaterEqual);
|
||||||
|
if symbol == "<=" return OptionalSymbol.Some(Symbol.LesserEqual);
|
||||||
|
if symbol == "%" return OptionalSymbol.Some(Symbol.Percent);
|
||||||
|
if symbol == "~" return OptionalSymbol.Some(Symbol.Tilde);
|
||||||
|
if symbol == "^" return OptionalSymbol.Some(Symbol.Carot);
|
||||||
|
assert false, "Unimplemented symbol '%s'" % symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
func symbol_to_str(symbol: Symbol) -> str {
|
||||||
|
match symbol in {
|
||||||
|
case Open return "(";
|
||||||
|
case Close return ")";
|
||||||
|
case OpenCurly return "{";
|
||||||
|
case CloseCurly return "}";
|
||||||
|
case Comma return ",";
|
||||||
|
case OpenSquare return "[";
|
||||||
|
case CloseSquare return "]";
|
||||||
|
case Colon return ":";
|
||||||
|
case Left return "<";
|
||||||
|
case Right return ">";
|
||||||
|
case Arrow return "->";
|
||||||
|
case Semicolon return ";";
|
||||||
|
case Equal return "=";
|
||||||
|
case Dequal return "==";
|
||||||
|
case Exclamation return "!";
|
||||||
|
case NotEqual return "!=";
|
||||||
|
case Dot return ".";
|
||||||
|
case Plus return "+";
|
||||||
|
case Dash return "-";
|
||||||
|
case Asterisk return "*";
|
||||||
|
case Dasterisk return "**";
|
||||||
|
case Slash return "/";
|
||||||
|
case QuestionMark return "?";
|
||||||
|
case Ampersand return "&";
|
||||||
|
case Dampersand return "&&";
|
||||||
|
case Pipe return "|";
|
||||||
|
case Dpipe return "||";
|
||||||
|
case Dleft return "<<";
|
||||||
|
case Dright return ">>";
|
||||||
|
case GreaterEqual return ">=";
|
||||||
|
case LesserEqual return "<=";
|
||||||
|
case Percent return "%";
|
||||||
|
case Tilde return "~";
|
||||||
|
case Carot return "^";
|
||||||
|
}
|
||||||
|
assert false, "Invalid symbol";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TokenContents {
|
||||||
|
Keyword(Keyword),
|
||||||
|
Identifier(str),
|
||||||
|
Number(int),
|
||||||
|
String(str),
|
||||||
|
Symbol(Symbol),
|
||||||
|
Eof
|
||||||
|
}
|
||||||
|
|
||||||
|
func token_contents_to_str(token: TokenContents) -> str {
|
||||||
|
match token in {
|
||||||
|
case Keyword(keyword) return "Keyword(%s)" % keyword_to_str(keyword);
|
||||||
|
case Identifier(string) return "Identifier(%s)" % string;
|
||||||
|
case Number(number) return "Number(%d)" % number;
|
||||||
|
case String(string) return "String(\"%s\")" % string;
|
||||||
|
case Symbol(symbol) return "Symbol('%s')" % symbol_to_str(symbol);
|
||||||
|
case Eof return "Eof";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Token {
|
||||||
|
line: int,
|
||||||
|
col: int,
|
||||||
|
value: str,
|
||||||
|
contents: TokenContents
|
||||||
|
}
|
||||||
|
|
||||||
|
func token_to_str(token: Token) -> str {
|
||||||
|
return token_contents_to_str(token.contents)+":"+int_to_str(token.line)+":"+int_to_str(token.col);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OptionalToken {
|
||||||
|
Some(Token),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func is_some_token(maybe_token: OptionalToken) -> bool {
|
||||||
|
match maybe_token in {
|
||||||
|
case Some(_) return true;
|
||||||
|
case None return false;
|
||||||
|
}
|
||||||
|
}
|
104
ppp_tokens.py
Normal file
104
ppp_tokens.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List, Literal, Tuple, Union
|
||||||
|
|
||||||
|
|
||||||
|
class Keyword(Enum):
|
||||||
|
Enum = 'enum'
|
||||||
|
Struct = 'struct'
|
||||||
|
Func = 'func'
|
||||||
|
If = 'if'
|
||||||
|
Else = 'else'
|
||||||
|
While = 'while'
|
||||||
|
Break = 'break'
|
||||||
|
Continue = 'continue'
|
||||||
|
Do = 'do'
|
||||||
|
For = 'for'
|
||||||
|
To = 'to'
|
||||||
|
In = 'in'
|
||||||
|
Match = 'match'
|
||||||
|
Case = 'case'
|
||||||
|
Assert = 'assert'
|
||||||
|
Return = 'return'
|
||||||
|
Lambda = 'lambda'
|
||||||
|
Import = 'import'
|
||||||
|
Type = 'type'
|
||||||
|
|
||||||
|
class Symbol(Enum):
|
||||||
|
Open = '('
|
||||||
|
Close = ')'
|
||||||
|
OpenCurly = '{'
|
||||||
|
CloseCurly = '}'
|
||||||
|
Comma = ','
|
||||||
|
OpenSquare = '['
|
||||||
|
CloseSquare = ']'
|
||||||
|
Colon = ':'
|
||||||
|
Left = '<'
|
||||||
|
Right = '>'
|
||||||
|
Arrow = '->'
|
||||||
|
EqualArrow = '=>'
|
||||||
|
Semicolon = ';'
|
||||||
|
Equal = '='
|
||||||
|
Dequal = '=='
|
||||||
|
Exclamation = '!'
|
||||||
|
NotEqual = '!='
|
||||||
|
Dot = '.'
|
||||||
|
Plus = '+'
|
||||||
|
Dash = '-'
|
||||||
|
Asterisk = '*'
|
||||||
|
Dasterisk = '**'
|
||||||
|
Slash = '/'
|
||||||
|
QuestionMark = '?'
|
||||||
|
Ampersand = '&'
|
||||||
|
Dampersand = '&&'
|
||||||
|
Pipe = '|'
|
||||||
|
Dpipe = '||'
|
||||||
|
Dleft = '<<'
|
||||||
|
Dright = '>>'
|
||||||
|
GreaterEqual = '>='
|
||||||
|
LesserEqual = '<='
|
||||||
|
Percent = '%'
|
||||||
|
Tilde = '~'
|
||||||
|
Carot = '^'
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class KeywordToken:
|
||||||
|
keyword: Keyword
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IdentifierToken:
|
||||||
|
identifier: str
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NumberToken:
|
||||||
|
number: int
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StringToken:
|
||||||
|
string: str
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SymbolToken:
|
||||||
|
symbol: Symbol
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(('symbol', self.symbol))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EofToken: pass
|
||||||
|
|
||||||
|
TokenContents = Union[
|
||||||
|
KeywordToken,
|
||||||
|
IdentifierToken,
|
||||||
|
NumberToken,
|
||||||
|
StringToken,
|
||||||
|
SymbolToken,
|
||||||
|
EofToken
|
||||||
|
]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Token:
|
||||||
|
line: int
|
||||||
|
col: int
|
||||||
|
value: str
|
||||||
|
contents: TokenContents
|
130
ppp_types.ppp
Normal file
130
ppp_types.ppp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
enum Type {
|
||||||
|
Int,
|
||||||
|
Str,
|
||||||
|
Bool,
|
||||||
|
Void,
|
||||||
|
Type,
|
||||||
|
Tuple(Type[]),
|
||||||
|
List(Type),
|
||||||
|
Array(Type, int),
|
||||||
|
Function(Type[], Type),
|
||||||
|
Union(Type[]),
|
||||||
|
Object,
|
||||||
|
Return(Type),
|
||||||
|
Enum(str, dict[str, Type[]], Type[]),
|
||||||
|
EnumStruct(Type, str),
|
||||||
|
Struct(str, dict[str, Type], Type[]),
|
||||||
|
Variable(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func type_represent(type_: Type) -> str {
|
||||||
|
match type_ in {
|
||||||
|
case Int return "int";
|
||||||
|
case Str return "str";
|
||||||
|
case Bool return "bool";
|
||||||
|
case Void return "void";
|
||||||
|
case Type return "type";
|
||||||
|
case Function(arguments, return_type) return "(%s) -> %s" % (join_by(", ", [type_represent(type_) for type_ in arguments]), type_represent(return_type));
|
||||||
|
case Enum(name, _, generics) do {
|
||||||
|
assert len(generics) == 0;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
case Variable(name) return name + "?";
|
||||||
|
case Struct(name, _, generics) do {
|
||||||
|
assert len(generics) == 0;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
case List(type_) return type_represent(type_) + "[]";
|
||||||
|
case Tuple(types) return "(" + join_by(", ", [type_represent(type_) for type_ in types]) + ")";
|
||||||
|
case _ do {
|
||||||
|
debug_print(type_);
|
||||||
|
assert false, "type_represent unimplemented";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func type_eq(a_type: Type, b_type: Type) -> bool return type_is_subtype_of(a_type, b_type) && type_is_subtype_of(b_type, a_type);
|
||||||
|
|
||||||
|
func type_is_subtype_of(type_a: Type, type_b: Type) -> bool {
|
||||||
|
match type_a in {
|
||||||
|
case Type match type_b in {
|
||||||
|
case Type return true;
|
||||||
|
case _ return false;
|
||||||
|
}
|
||||||
|
case Function(a_arguments, a_return_type) match type_b in {
|
||||||
|
case Function(b_arguments, b_return_type) do {
|
||||||
|
assert len(a_arguments) == len(b_arguments);
|
||||||
|
for i in range(0, len(a_arguments)) if !type_is_subtype_of(b_arguments[i], a_arguments[i]) return false;
|
||||||
|
return type_is_subtype_of(a_return_type, b_return_type);
|
||||||
|
}
|
||||||
|
case _ assert false;
|
||||||
|
}
|
||||||
|
case Str match type_b in {
|
||||||
|
case Str return true;
|
||||||
|
case _ return false;
|
||||||
|
}
|
||||||
|
case Enum(a_name, _, a_generics) match type_b in {
|
||||||
|
case Enum(b_name, _, b_generics) do {
|
||||||
|
assert len(a_generics) == 0;
|
||||||
|
assert len(b_generics) == 0;
|
||||||
|
return a_name == b_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Struct(a_name, a_members, a_generics) match type_b in {
|
||||||
|
case Struct(b_name, b_members, b_generics) do {
|
||||||
|
assert len(a_generics) == 0;
|
||||||
|
assert len(b_generics) == 0;
|
||||||
|
return a_name == b_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Bool match type_b in {
|
||||||
|
case Bool return true;
|
||||||
|
case _ return false;
|
||||||
|
}
|
||||||
|
case List(a_type) match type_b in {
|
||||||
|
case List(b_type) do {
|
||||||
|
match a_type in { case Variable(_) return true; }
|
||||||
|
match b_type in { case Variable(_) return true; }
|
||||||
|
return type_eq(a_type, b_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Tuple(a_types) match type_b in {
|
||||||
|
case Tuple(b_types) do {
|
||||||
|
if len(a_types) != len(b_types) return false;
|
||||||
|
for i in range(0, len(a_types)) if !type_is_subtype_of(a_types[i], b_types[i]) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case _ return false;
|
||||||
|
}
|
||||||
|
case _ assert false, "Unimplemented type_is_subtype_of %s, %s" % (type_represent(type_a), type_represent(type_b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func type_fill(type_: Type, types: dict[str, Type], stack: int[]) -> Type {
|
||||||
|
type_id: int = id(type_);
|
||||||
|
new_stack: int[] = stack+[type_id];
|
||||||
|
match type_ in {
|
||||||
|
case Int return Type.Int;
|
||||||
|
case Str return Type.Str;
|
||||||
|
case Bool return Type.Bool;
|
||||||
|
case Enum(name, members, generics) do {
|
||||||
|
for id_ in stack if id_ == type_id return type_;
|
||||||
|
assert len(generics) == 0, "Unimplemented type_fill enum generics";
|
||||||
|
for member in members members[member] = [type_fill(element, types, new_stack) for element in members[member]];
|
||||||
|
for i in range(0, len(generics)) generics[i] = type_fill(generics[i], types, new_stack);
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
case Variable(name) do {
|
||||||
|
for type_name in types if type_name == name return types[name];
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
case Struct(name, members, generics) do {
|
||||||
|
assert len(generics) == 0;
|
||||||
|
for field in members members[field] = type_fill(members[field], types, new_stack);
|
||||||
|
return type_;
|
||||||
|
}
|
||||||
|
case List(type_) return Type.List(type_fill(type_, types, new_stack));
|
||||||
|
case Tuple(types_) return Type.Tuple([type_fill(type_, types, new_stack) for type_ in types_]);
|
||||||
|
case _ assert false, "Unimplemented type_fill %s" % type_represent(type_);
|
||||||
|
}
|
||||||
|
}
|
306
ppp_types.py
Normal file
306
ppp_types.py
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import Dict, List, Tuple, Union
|
||||||
|
|
||||||
|
import sys
|
||||||
|
sys.setrecursionlimit(1000)
|
||||||
|
|
||||||
|
class Type(ABC):
|
||||||
|
def is_indexable(self) -> bool: return False
|
||||||
|
|
||||||
|
def is_subtype_of(self, other: 'Type') -> bool:
|
||||||
|
match self, other:
|
||||||
|
case StrType(), StrType(): return True
|
||||||
|
case TypeType_(), TypeType_(): return True
|
||||||
|
case FunctionType(self_arguments, self_return_type), FunctionType(other_arguments, other_return_type):
|
||||||
|
assert len(self_arguments) == len(other_arguments)
|
||||||
|
for (self_argument, other_argument) in zip(self_arguments, other_arguments):
|
||||||
|
if not other_argument.is_subtype_of(self_argument): return False
|
||||||
|
return self_return_type.is_subtype_of(other_return_type)
|
||||||
|
case EnumType(self_name, self_members), EnumType(other_name, other_members):
|
||||||
|
# if self_name == other_name: assert self is other, (num_expressions, self, other, self_name, other_name, self_members, other_members)
|
||||||
|
return self is other
|
||||||
|
return self_name == other_name
|
||||||
|
case StructType(_, _), StructType(_, _): return self is other
|
||||||
|
case VoidType(), VoidType(): return True
|
||||||
|
case ListType(self_type), ListType(other_type):
|
||||||
|
# TODO: Maybe return which types match
|
||||||
|
if isinstance(self_type, VariableType): return True
|
||||||
|
if isinstance(other_type, VariableType): return True
|
||||||
|
return self_type == other_type
|
||||||
|
return self_type.is_subtype_of(other_type)
|
||||||
|
case TupleType(self_elememts), TupleType(other_elements):
|
||||||
|
if len(self_elememts) != len(other_elements): return False
|
||||||
|
for (self_element, other_element) in zip(self_elememts, other_elements):
|
||||||
|
if not self_element.is_subtype_of(other_element): return False
|
||||||
|
return True
|
||||||
|
case IntType(), IntType():
|
||||||
|
return True
|
||||||
|
case VariableType(self_name), VariableType(other_name):
|
||||||
|
return self_name == other_name
|
||||||
|
case _, VariableType(""): return True
|
||||||
|
case type, UnionType(types):
|
||||||
|
for union_type in types:
|
||||||
|
if type.is_subtype_of(union_type): return True
|
||||||
|
return False
|
||||||
|
case BoolType(), BoolType(): return True
|
||||||
|
case DictionaryType(self_key_type, self_value_type), DictionaryType(other_key_type, other_value_type):
|
||||||
|
if isinstance(self_key_type, VariableType) and self_key_type.name == "" and isinstance(self_value_type, VariableType) and self_value_type.name == "": return True
|
||||||
|
return other_key_type.is_subtype_of(self_key_type) and self_value_type.is_subtype_of(other_value_type)
|
||||||
|
case type, ObjectType(): return True
|
||||||
|
case type_a, type_b if type_a.__class__ != type_b.__class__: return False
|
||||||
|
case _, _: assert False, ("Unimplemented", self, other)
|
||||||
|
assert False, ("Unimplemented", self, other)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Type) and self.is_subtype_of(other) and other.is_subtype_of(self)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def represent(self) -> str: ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def fill(self, types: 'Dict[str, Type]', stack: List[int]) -> 'Type': ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def new_fill(self, types: 'Dict[str, Type]', stack: List[int]) -> 'Tuple[bool, Type]': ...
|
||||||
|
|
||||||
|
def new_fill_list(self, type_list: 'List[Type]', types: 'Dict[str, Type]', stack: List[int]) -> 'Tuple[bool, List[Type]]':
|
||||||
|
new_types = [type.new_fill(types, stack+[id(self)]) for type in type_list]
|
||||||
|
is_new = any([new_type[0] for new_type in new_types])
|
||||||
|
return (is_new, [new_type[1] for new_type in new_types])
|
||||||
|
|
||||||
|
def new_fill_dict(self, type_dict: 'Dict[str, Type]', types: 'Dict[str, Type]', stack: List[int]) -> 'Tuple[bool, Dict[str, Type]]':
|
||||||
|
new_types = {field: type_dict[field].new_fill(types, stack+[id(self)]) for field in type_dict}
|
||||||
|
is_new = any([new_types[field][0] for field in new_types])
|
||||||
|
return (is_new, {field: new_types[field][1] for field in new_types})
|
||||||
|
|
||||||
|
class Primitive(Type):
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type: return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]: return (False, self)
|
||||||
|
|
||||||
|
class IntType(Primitive):
|
||||||
|
def represent(self) -> str: return 'int'
|
||||||
|
Int = IntType()
|
||||||
|
|
||||||
|
|
||||||
|
class StrType(Primitive):
|
||||||
|
def is_indexable(self) -> bool: return True
|
||||||
|
|
||||||
|
def represent(self) -> str: return 'str'
|
||||||
|
Str = StrType()
|
||||||
|
|
||||||
|
|
||||||
|
class BoolType(Primitive):
|
||||||
|
def represent(self) -> str: return 'bool'
|
||||||
|
Bool = BoolType()
|
||||||
|
|
||||||
|
|
||||||
|
class VoidType(Primitive):
|
||||||
|
def represent(self) -> str: return 'void'
|
||||||
|
Void = VoidType()
|
||||||
|
|
||||||
|
|
||||||
|
class TypeType_(Primitive):
|
||||||
|
def represent(self) -> str: return 'type'
|
||||||
|
TypeType = TypeType_()
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TupleType(Type):
|
||||||
|
types: List[Type]
|
||||||
|
|
||||||
|
def is_indexable(self) -> bool: return True
|
||||||
|
|
||||||
|
def represent(self) -> str: return '('+', '.join([type.represent() for type in self.types])+')'
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.types = [type.fill(types, stack+[id(self)]) for type in self.types]
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
is_new, new_types = self.new_fill_list(self.types, types, stack)
|
||||||
|
return (is_new, TupleType(new_types))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ListType(Type):
|
||||||
|
type: Type
|
||||||
|
|
||||||
|
def is_indexable(self) -> bool: return True
|
||||||
|
|
||||||
|
def represent(self) -> str: return self.type.represent()+'[]'
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.type = self.type.fill(types, stack+[id(self)])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
assert id(self) not in stack
|
||||||
|
is_new, new_type = self.type.new_fill(types, stack+[id(self)])
|
||||||
|
return (is_new, ListType(new_type))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ArrayType(Type):
|
||||||
|
type: Type
|
||||||
|
number: int
|
||||||
|
|
||||||
|
def is_indexable(self) -> bool: return True
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionType(Type):
|
||||||
|
arguments: List[Type]
|
||||||
|
return_type: Type
|
||||||
|
|
||||||
|
def represent(self) -> str: return '('+', '.join([type.represent() for type in self.arguments])+') -> '+self.return_type.represent()
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.arguments = [argument.fill(types, stack+[id(self)]) for argument in self.arguments]
|
||||||
|
self.return_type = self.return_type.fill(types, stack+[id(self)])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
assert id(self) not in stack # TODO: Wtf?
|
||||||
|
is_new_arguments, new_arguments = self.new_fill_list(self.arguments, types, stack)
|
||||||
|
is_new_return_type, new_return_type = self.return_type.new_fill(types, stack+[id(self)])
|
||||||
|
return (is_new_arguments or is_new_return_type, FunctionType(new_arguments, new_return_type))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class UnionType(Type):
|
||||||
|
types: List[Type]
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.types = [type.fill(types, stack+[id(self)]) for type in self.types]
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
is_new, new_types = self.new_fill_list(self.types, types, stack)
|
||||||
|
return (is_new, UnionType(new_types))
|
||||||
|
|
||||||
|
def represent(self) -> str: return '('+'|'.join([type.represent() for type in self.types])+')'
|
||||||
|
|
||||||
|
class ObjectType(Primitive):
|
||||||
|
def represent(self) -> str: return 'object'
|
||||||
|
Object = ObjectType()
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ReturnType(Type):
|
||||||
|
type: Type
|
||||||
|
|
||||||
|
def represent(self) -> str: return f"return<{self.type.represent()}>"
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.type = self.type.fill(types, stack+[id(self)])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
assert id(self) not in stack
|
||||||
|
is_new, new_type = self.type.new_fill(types, stack+[id(self)])
|
||||||
|
return (is_new, ReturnType(new_type))
|
||||||
|
|
||||||
|
|
||||||
|
num_expressions: int = 0
|
||||||
|
@dataclass
|
||||||
|
class EnumType(Type):
|
||||||
|
name: str
|
||||||
|
members: Dict[str, List[Type]]
|
||||||
|
generics: List[Type]
|
||||||
|
|
||||||
|
def __repr__(self) -> str: return self.represent()
|
||||||
|
|
||||||
|
def represent(self) -> str: return self.name+('['+', '.join([generic.represent() for generic in self.generics])+']' if self.generics else '')
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.members = {member_name: [element.fill(types, stack+[id(self)]) for element in self.members[member_name]] for member_name in self.members}
|
||||||
|
self.generics = [type.fill(types, stack+[id(self)]) for type in self.generics]
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
assert id(self) not in stack
|
||||||
|
is_new = False
|
||||||
|
new_members: Dict[str, List[Type]] = {}
|
||||||
|
for member_name in self.members:
|
||||||
|
member = self.members[member_name]
|
||||||
|
is_new_member, new_members[member_name] = self.new_fill_list(member, types, stack)
|
||||||
|
is_new = is_new or is_new_member
|
||||||
|
return (is_new, EnumType(self.name, new_members, self.generics) if is_new else self)
|
||||||
|
|
||||||
|
def __hash__(self) -> int:
|
||||||
|
return hash(('type', 'enum', self.name, tuple([(member, tuple(self.members[member])) for member in self.members]), tuple(self.generics)))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StructType(Type):
|
||||||
|
name: str
|
||||||
|
members: Dict[str, Type]
|
||||||
|
generics: List[Type]
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
assert not self.generics
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
for field in self.members:
|
||||||
|
self.members[field] = self.members[field].fill(types, stack+[id(self)])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
assert id(self) not in stack
|
||||||
|
is_new, new_members = self.new_fill_dict(self.members, types, stack)
|
||||||
|
return (is_new, StructType(self.name, new_members, self.generics) if is_new else self)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class VariableType(Type):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
def represent(self) -> str: return self.name + '?'
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
return types.get(self.name, self)
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
return (self.name in types, types.get(self.name, self))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DictionaryType(Type):
|
||||||
|
key_type: Type
|
||||||
|
value_type: Type
|
||||||
|
|
||||||
|
def represent(self) -> str: return f"dict[{self.key_type.represent()}, {self.value_type.represent()}]"
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.key_type = self.key_type.fill(types, stack+[id(self)])
|
||||||
|
self.value_type = self.value_type.fill(types, stack+[id(self)])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
is_new_key, new_key_type = self.key_type.new_fill(types, stack+[id(self)])
|
||||||
|
is_new_value, new_value_type = self.value_type.new_fill(types, stack+[id(self)])
|
||||||
|
return (is_new_key or is_new_value, DictionaryType(new_key_type, new_value_type))
|
||||||
|
|
||||||
|
def is_indexable(self) -> bool: return True
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GenericType(Type):
|
||||||
|
variables: List[VariableType]
|
||||||
|
type: Type
|
||||||
|
|
||||||
|
def represent(self) -> str: assert False
|
||||||
|
|
||||||
|
def fill(self, types: Dict[str, Type], stack: List[int]) -> Type:
|
||||||
|
if id(self) in stack: return self
|
||||||
|
self.type = self.type.fill(types, stack+[id(self)])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def new_fill(self, types: Dict[str, Type], stack: List[int]) -> Tuple[bool, Type]:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
def substitute(self, types: List[Type]) -> Type:
|
||||||
|
assert len(types) == len(self.variables), f"{self.type.represent()} expected {len(self.variables)} type parameters, but got {len(types)}!"
|
||||||
|
return self.type.new_fill({variable.name: type for (variable, type) in zip(self.variables, types)}, [])[1]
|
180
ppp_types0.py
Normal file
180
ppp_types0.py
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from types import EllipsisType
|
||||||
|
from typing import Dict, Optional, Union as Union_, Tuple as Tuple_, List as List_
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StructContents:
|
||||||
|
generics: 'List_[Type]'
|
||||||
|
members: 'Dict[str, Type]'
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class TupleContents:
|
||||||
|
elements: 'List_[Type]'
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EnumContents:
|
||||||
|
generics: 'List_[Type]'
|
||||||
|
members: 'Dict[str, Union_[Dict[str, Type], List_[Type]]]'
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class UnknownContents:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionContents:
|
||||||
|
arguments: 'List_[Type]'
|
||||||
|
return_type: 'Type'
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ListContents:
|
||||||
|
type: 'Optional[Type]'
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class UnionContents:
|
||||||
|
types: 'List_[Type]'
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Type:
|
||||||
|
name: str
|
||||||
|
contents: Union_[EnumContents, StructContents, TupleContents, FunctionContents, ListContents, UnknownContents, UnionContents]
|
||||||
|
|
||||||
|
def specify(self, *types: 'Type') -> 'Type':
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
def is_subtype_of(self, other: 'Type') -> bool: # TODO: Maybe return any generics that match
|
||||||
|
if other.name == 'object': return True
|
||||||
|
match self.contents, other.contents:
|
||||||
|
case (EnumContents(self_members), EnumContents(other_members)) | (StructContents(self_members), StructContents(other_members)):
|
||||||
|
return self.name == other.name
|
||||||
|
case TupleContents(self_elements), TupleContents(other_elements):
|
||||||
|
if self.name != other.name: return False
|
||||||
|
if len(self_elements) != len(other_elements): return False
|
||||||
|
for (self_element, other_element) in zip(self_elements, other_elements):
|
||||||
|
if not self_element.is_subtype_of(other_element): return False
|
||||||
|
return True
|
||||||
|
case FunctionContents(self_arguments, self_return_type), FunctionContents(other_arguments, other_return_type):
|
||||||
|
for (self_argument, other_argument) in zip(self_arguments, other_arguments):
|
||||||
|
if not other_argument.is_subtype_of(self_argument): return False
|
||||||
|
return self_return_type.is_subtype_of(other_return_type)
|
||||||
|
case ListContents(self_type), ListContents(other_type):
|
||||||
|
if self_type is None: return True
|
||||||
|
if other_type is None: return True
|
||||||
|
return self_type.is_subtype_of(other_type)
|
||||||
|
case UnionContents(self_types), UnionContents(other_types):
|
||||||
|
for type_ in self_types:
|
||||||
|
if not type_.is_subtype_of(other): return False
|
||||||
|
return True
|
||||||
|
case a, b if type(a) == type(b):
|
||||||
|
assert False, ("Unimplemented", self, other)
|
||||||
|
case _, UnionContents(types):
|
||||||
|
for type_ in types:
|
||||||
|
if self.is_subtype_of(type_): return True
|
||||||
|
return False
|
||||||
|
case _, _:
|
||||||
|
return False
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
def fill(self, types: 'Dict[str, Type]') -> 'Type':
|
||||||
|
match self.contents:
|
||||||
|
case TupleContents(elements):
|
||||||
|
return Type(self.name, TupleContents([type.fill(types) for type in elements]))
|
||||||
|
case EnumContents(generics, members):
|
||||||
|
assert not generics # TODO
|
||||||
|
|
||||||
|
new_members: Dict[str, Union_[Dict[str, Type], List_[Type]]] = {}
|
||||||
|
for name in members:
|
||||||
|
member = members[name]
|
||||||
|
if isinstance(member, list):
|
||||||
|
new_members[name] = [type.fill(types) for type in member]
|
||||||
|
elif isinstance(member, dict):
|
||||||
|
new_members[name] = {field: member[field].fill(types) for field in member}
|
||||||
|
else:
|
||||||
|
assert False, "Unreachable"
|
||||||
|
|
||||||
|
return Type(self.name, EnumContents(generics, new_members))
|
||||||
|
case StructContents(generics, members):
|
||||||
|
assert not generics # TODO
|
||||||
|
return Type(self.name, StructContents(generics, {field: members[field].fill(types) for field in members}))
|
||||||
|
case ListContents(type):
|
||||||
|
return Type(self.name, ListContents(type.fill(types) if type else None))
|
||||||
|
case UnknownContents():
|
||||||
|
return types[self.name] if self.name in types else self
|
||||||
|
case UnionContents(types_):
|
||||||
|
return Type(self.name, UnionContents([type.fill(types) for type in types_]))
|
||||||
|
case FunctionContents(arguments, return_type):
|
||||||
|
return Type(self.name, FunctionContents([argument.fill(types) for argument in arguments], return_type.fill(types)))
|
||||||
|
case _:
|
||||||
|
assert False, ("Unimplemented", self.contents)
|
||||||
|
assert False, "Unreachable"
|
||||||
|
|
||||||
|
def represent(self) -> str:
|
||||||
|
match self.contents:
|
||||||
|
case EnumContents(generics, _) | StructContents(generics, _): return self.name + ("<"+', '.join([generic.represent() for generic in generics])+">" if generics else '')
|
||||||
|
case TupleContents(elements): return (self.name if self.name != "tuple" else '') + ('('+', '.join([generic.represent() for generic in elements])+')' if elements else '')
|
||||||
|
case ListContents(type): return (type.represent() if type else '')+'[]'
|
||||||
|
case UnknownContents(): return self.name+"?"
|
||||||
|
case UnionContents(types): return '('+'|'.join([type.represent() for type in types])+')'
|
||||||
|
case FunctionContents(arguments, return_type): return "("+', '.join([type.represent() for type in arguments])+") -> "+return_type.represent()
|
||||||
|
assert False, ("Unimplemented")
|
||||||
|
|
||||||
|
def __eq__(self, other) -> bool:
|
||||||
|
return isinstance(other, Type) and self.is_subtype_of(other) and other.is_subtype_of(self)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
try:
|
||||||
|
return self.represent()
|
||||||
|
except AssertionError:
|
||||||
|
return f"Unimplemented {self.contents.__class__.__name__}"
|
||||||
|
|
||||||
|
def primitive(name: str) -> Type: return Type(name, TupleContents([]))
|
||||||
|
Int = primitive("int")
|
||||||
|
Str = primitive("str")
|
||||||
|
Bool = primitive("bool")
|
||||||
|
Void = primitive("void")
|
||||||
|
TypeType = primitive("type")
|
||||||
|
|
||||||
|
def Tuple(*types: Type) -> Type: return Type("tuple", TupleContents(list(types)))
|
||||||
|
def List(type: Optional[Type]=None) -> Type: return Type("list", ListContents(type))
|
||||||
|
|
||||||
|
def Function(*arguments_and_return_type: Type) -> Type:
|
||||||
|
assert arguments_and_return_type, "Must have a return value"
|
||||||
|
return Type("function", FunctionContents(list(arguments_and_return_type[:-1]), arguments_and_return_type[-1]))
|
||||||
|
|
||||||
|
Unit = Tuple()
|
||||||
|
|
||||||
|
def Union(*types: Type) -> Type: return Type("union", UnionContents(list(types)))
|
||||||
|
def Return(type: Type) -> Type:
|
||||||
|
return Type('return', StructContents([type], {'value': type}))
|
||||||
|
|
||||||
|
Object = primitive('object')
|
||||||
|
|
||||||
|
# TODO: struct enum members
|
||||||
|
def EnumMember(enum_type: Type, *types: Type) -> Type: return Type('enum_tuple_member', FunctionContents(list(types), enum_type))
|
||||||
|
|
||||||
|
# def issubclassof(type1: Type, type2: Type) -> bool:
|
||||||
|
# if type1.name != 'union' and type2.name == 'union':
|
||||||
|
# return type1 in type2.generics
|
||||||
|
# if type1.name != type2.name: return False
|
||||||
|
# if not type2.generics: return True
|
||||||
|
# if len(type1.generics) != len(type2.generics): return False
|
||||||
|
# if type1.name == '->':
|
||||||
|
# for (type_a, type_b) in zip(type1.generics[:-1], type2.generics[:-1]):
|
||||||
|
# if not issubclassof(type_b, type_a): return False
|
||||||
|
# return issubclassof(type1.generics[-1], type2.generics[-1])
|
||||||
|
# if type1.name == 'union': assert False, ("Unimplemented", type1, type2)
|
||||||
|
# if type1.name == 'tuple': return all([issubclassof(type_a, type_b) for (type_a, type_b) in zip(type1.generics, type2.generics)])
|
||||||
|
# assert False, ("Unimplemented", type1, type2)
|
||||||
|
|
||||||
|
# Void = Type('void', TupleContents([]))
|
||||||
|
# AbstractFunction = Type('->', [])
|
||||||
|
# def Function(*arguments_and_return_type: Type) -> Type:
|
||||||
|
# assert arguments_and_return_type, "Must have a return value"
|
||||||
|
# return AbstractFunction.specify(*arguments_and_return_type)
|
||||||
|
|
||||||
|
# Int = Type('int', [], False)
|
||||||
|
# String = Type('str', [], False)
|
||||||
|
# Bool = Type('bool', [], False)
|
||||||
|
# def List(type: Type) -> Type: return Type('list', [type])
|
||||||
|
|
||||||
|
# Unit = Type('unit', [], False)
|
||||||
|
# def Tuple(*types: Type) -> Type: return Type('tuple', list(types)) if types else Unit
|
27
type-inference.md
Normal file
27
type-inference.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Type Inference + Generics
|
||||||
|
|
||||||
|
The interpreter needs to evaluate expressions with an expectation of what type they will result in. For example, if it encounters an empty list expecting a list of ints, then it will return an empty list but the type information will convey that it is a list of ints. Currently the way it works is that it just returns a list where the internal type of the list is an empty variable type, which has been hard-coded to be a subclass of any list and have any list be a subclass of that type. Evaluating with knowledge of the expected type will also allow for enum members to be returned or referenced with a '.' + the enum member name, rather than having to name the enum type itself every single time.
|
||||||
|
|
||||||
|
## Generics + Generic functions
|
||||||
|
|
||||||
|
Maybe I can also remove the 'object' type. I could have any function that can take in any opject just use a generic type parameter. E.g., The `len` function can be of type `A[] -> int`, where `A` is just not used in the return_type. Maybe for generic functions I will need to have the definition indicate it. E.g.,
|
||||||
|
```
|
||||||
|
func len<A> (list: A[]) -> int { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
You could probably still call the function without needing to specify `A`. E.g.,
|
||||||
|
```
|
||||||
|
ints: int[] = [0, 1, 2, 3, 4, 5]
|
||||||
|
num_ints = len(ints);
|
||||||
|
```
|
||||||
|
|
||||||
|
You won't need to, but still can do this:
|
||||||
|
```
|
||||||
|
num_ints = len<int>(ints);
|
||||||
|
```
|
||||||
|
|
||||||
|
It would probably quite hard to parse, however. How would the parser know the difference between that first `<` and a less than comparison between some variable `len` and some variable `int`? It would need to at some point start parsing for a type, but when will it know that? `len<int>(ints)` can be justifiably parsed as `len` less than `int` greater than `ints`, where `ints` has been wrapped in parentheses and you are comparing a booling to a list. But of course, the parser has no knowledge of what types expressions are. A similar situation would occur if I did `len[int](ints)` instead. It could be parsed as array `len` element number `int`, called with the argument `ints`.
|
||||||
|
|
||||||
|
Should I even allow the user to specify the type for a generic function? What would they need that for. Maybe they need to return an array of a higher type a type inferer would infer. For example, they need a function to return a `object[]`, and it just so happens that they know that they start of with a `int[]`, but will add objects later, or instead of `int` and `object`, any two types where the former is a subtype of the latter.
|
||||||
|
|
||||||
|
Um, actually. I think what I'll do is have the language infer the types for any generic functions, and then convert the return value to any types if you indicate it.
|
Loading…
Reference in New Issue
Block a user