added first steps on pbs
This commit is contained in:
parent
801109b993
commit
14da21fe9f
@ -73,7 +73,7 @@ Active keywords in `.pbs` files (v0 Core):
|
|||||||
- `import`, `from`, `as`
|
- `import`, `from`, `as`
|
||||||
- `service`, `fn`
|
- `service`, `fn`
|
||||||
- `declare`, `struct`, `contract`, `error`
|
- `declare`, `struct`, `contract`, `error`
|
||||||
- `let`, `mut`
|
- `let`
|
||||||
- `if`, `else`, `when`, `for`, `in`, `return`
|
- `if`, `else`, `when`, `for`, `in`, `return`
|
||||||
- `true`, `false`
|
- `true`, `false`
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ ServiceMember ::= 'fn' Identifier ParamList ReturnType? Block
|
|||||||
```ebnf
|
```ebnf
|
||||||
FunctionDecl ::= 'fn' Identifier ParamList ReturnType? ElseFallback? Block
|
FunctionDecl ::= 'fn' Identifier ParamList ReturnType? ElseFallback? Block
|
||||||
ParamList ::= '(' Param (',' Param)* ')'
|
ParamList ::= '(' Param (',' Param)* ')'
|
||||||
Param ::= 'mut'? Identifier ':' TypeRef
|
Param ::= Identifier ':' TypeRef
|
||||||
ReturnType ::= ':' TypeRef
|
ReturnType ::= ':' TypeRef
|
||||||
ElseFallback ::= 'else' Expr
|
ElseFallback ::= 'else' Expr
|
||||||
```
|
```
|
||||||
@ -248,7 +248,7 @@ Block ::= '{' Stmt* TailExpr? '}'
|
|||||||
Stmt ::= LetStmt | ReturnStmt | IfStmt | ForStmt | ExprStmt
|
Stmt ::= LetStmt | ReturnStmt | IfStmt | ForStmt | ExprStmt
|
||||||
TailExpr ::= Expr
|
TailExpr ::= Expr
|
||||||
|
|
||||||
LetStmt ::= 'let' 'mut'? Identifier (':' TypeRef)? '=' Expr ';'
|
LetStmt ::= 'let' Identifier (':' TypeRef)? '=' Expr ';'
|
||||||
ReturnStmt ::= 'return' Expr? ';'
|
ReturnStmt ::= 'return' Expr? ';'
|
||||||
ExprStmt ::= Expr ';'
|
ExprStmt ::= Expr ';'
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,6 @@ public final class PbsAst {
|
|||||||
|
|
||||||
public record Parameter(
|
public record Parameter(
|
||||||
String name,
|
String name,
|
||||||
boolean mutable,
|
|
||||||
TypeRef typeRef,
|
TypeRef typeRef,
|
||||||
Span span) {
|
Span span) {
|
||||||
}
|
}
|
||||||
@ -44,7 +43,6 @@ public final class PbsAst {
|
|||||||
|
|
||||||
public record LetStatement(
|
public record LetStatement(
|
||||||
String name,
|
String name,
|
||||||
boolean mutable,
|
|
||||||
TypeRef explicitType,
|
TypeRef explicitType,
|
||||||
Expression initializer,
|
Expression initializer,
|
||||||
Span span) implements Statement {
|
Span span) implements Statement {
|
||||||
|
|||||||
@ -10,6 +10,14 @@ import java.util.Map;
|
|||||||
public final class PbsLexer {
|
public final class PbsLexer {
|
||||||
private static final Map<String, PbsTokenKind> KEYWORDS = buildKeywords();
|
private static final Map<String, PbsTokenKind> KEYWORDS = buildKeywords();
|
||||||
|
|
||||||
|
private enum LexerState {
|
||||||
|
DEFAULT,
|
||||||
|
IDENTIFIER,
|
||||||
|
NUMBER,
|
||||||
|
STRING,
|
||||||
|
LINE_COMMENT
|
||||||
|
}
|
||||||
|
|
||||||
private final String source;
|
private final String source;
|
||||||
private final String sourceLabel;
|
private final String sourceLabel;
|
||||||
private final BuildingIssueSink issues;
|
private final BuildingIssueSink issues;
|
||||||
@ -17,6 +25,7 @@ public final class PbsLexer {
|
|||||||
|
|
||||||
private int start;
|
private int start;
|
||||||
private int current;
|
private int current;
|
||||||
|
private LexerState state = LexerState.DEFAULT;
|
||||||
|
|
||||||
private PbsLexer(final String source, final String sourceLabel, final BuildingIssueSink issues) {
|
private PbsLexer(final String source, final String sourceLabel, final BuildingIssueSink issues) {
|
||||||
this.source = source == null ? "" : source;
|
this.source = source == null ? "" : source;
|
||||||
@ -33,15 +42,24 @@ public final class PbsLexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlyList<PbsToken> lexInternal() {
|
private ReadOnlyList<PbsToken> lexInternal() {
|
||||||
while (!isAtEnd()) {
|
while (!isAtEnd() || state != LexerState.DEFAULT) {
|
||||||
start = current;
|
switch (state) {
|
||||||
scanToken();
|
case DEFAULT -> scanDefaultState();
|
||||||
|
case IDENTIFIER -> scanIdentifierState();
|
||||||
|
case NUMBER -> scanNumberState();
|
||||||
|
case STRING -> scanStringState();
|
||||||
|
case LINE_COMMENT -> scanLineCommentState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tokens.add(new PbsToken(PbsTokenKind.EOF, "", current, current));
|
tokens.add(new PbsToken(PbsTokenKind.EOF, "", current, current));
|
||||||
return ReadOnlyList.wrap(tokens);
|
return ReadOnlyList.wrap(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanToken() {
|
private void scanDefaultState() {
|
||||||
|
if (isAtEnd()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
start = current;
|
||||||
final char c = advance();
|
final char c = advance();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case ' ', '\r', '\t', '\n' -> {
|
case ' ', '\r', '\t', '\n' -> {
|
||||||
@ -88,22 +106,19 @@ public final class PbsLexer {
|
|||||||
}
|
}
|
||||||
case '/' -> {
|
case '/' -> {
|
||||||
if (match('/')) {
|
if (match('/')) {
|
||||||
// Line comment
|
state = LexerState.LINE_COMMENT;
|
||||||
while (!isAtEnd() && peek() != '\n') {
|
|
||||||
advance();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addToken(PbsTokenKind.SLASH);
|
addToken(PbsTokenKind.SLASH);
|
||||||
}
|
}
|
||||||
case '"' -> string();
|
case '"' -> state = LexerState.STRING;
|
||||||
default -> {
|
default -> {
|
||||||
if (isDigit(c)) {
|
if (isDigit(c)) {
|
||||||
number();
|
state = LexerState.NUMBER;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isIdentifierStart(c)) {
|
if (isIdentifierStart(c)) {
|
||||||
identifier();
|
state = LexerState.IDENTIFIER;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
report("E_LEX_INVALID_CHAR", "Invalid character: '%s'".formatted(c));
|
report("E_LEX_INVALID_CHAR", "Invalid character: '%s'".formatted(c));
|
||||||
@ -111,7 +126,7 @@ public final class PbsLexer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void identifier() {
|
private void scanIdentifierState() {
|
||||||
while (!isAtEnd() && isIdentifierPart(peek())) {
|
while (!isAtEnd() && isIdentifierPart(peek())) {
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
@ -119,9 +134,10 @@ public final class PbsLexer {
|
|||||||
final String text = source.substring(start, current);
|
final String text = source.substring(start, current);
|
||||||
final PbsTokenKind kind = KEYWORDS.getOrDefault(text, PbsTokenKind.IDENTIFIER);
|
final PbsTokenKind kind = KEYWORDS.getOrDefault(text, PbsTokenKind.IDENTIFIER);
|
||||||
addToken(kind);
|
addToken(kind);
|
||||||
|
state = LexerState.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void number() {
|
private void scanNumberState() {
|
||||||
while (!isAtEnd() && isDigit(peek())) {
|
while (!isAtEnd() && isDigit(peek())) {
|
||||||
advance();
|
advance();
|
||||||
}
|
}
|
||||||
@ -138,13 +154,15 @@ public final class PbsLexer {
|
|||||||
if (!isFloat && !isAtEnd() && peek() == 'b') {
|
if (!isFloat && !isAtEnd() && peek() == 'b') {
|
||||||
advance();
|
advance();
|
||||||
addToken(PbsTokenKind.BOUNDED_LITERAL);
|
addToken(PbsTokenKind.BOUNDED_LITERAL);
|
||||||
|
state = LexerState.DEFAULT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
addToken(isFloat ? PbsTokenKind.FLOAT_LITERAL : PbsTokenKind.INT_LITERAL);
|
addToken(isFloat ? PbsTokenKind.FLOAT_LITERAL : PbsTokenKind.INT_LITERAL);
|
||||||
|
state = LexerState.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void string() {
|
private void scanStringState() {
|
||||||
while (!isAtEnd() && peek() != '"') {
|
while (!isAtEnd() && peek() != '"') {
|
||||||
if (peek() == '\\' && !isAtEnd()) {
|
if (peek() == '\\' && !isAtEnd()) {
|
||||||
advance();
|
advance();
|
||||||
@ -158,12 +176,21 @@ public final class PbsLexer {
|
|||||||
|
|
||||||
if (isAtEnd()) {
|
if (isAtEnd()) {
|
||||||
report("E_LEX_UNTERMINATED_STRING", "Unterminated string literal");
|
report("E_LEX_UNTERMINATED_STRING", "Unterminated string literal");
|
||||||
|
state = LexerState.DEFAULT;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closing quote.
|
// Closing quote.
|
||||||
advance();
|
advance();
|
||||||
addToken(PbsTokenKind.STRING_LITERAL);
|
addToken(PbsTokenKind.STRING_LITERAL);
|
||||||
|
state = LexerState.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scanLineCommentState() {
|
||||||
|
while (!isAtEnd() && peek() != '\n') {
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
state = LexerState.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToken(final PbsTokenKind kind) {
|
private void addToken(final PbsTokenKind kind) {
|
||||||
@ -224,7 +251,6 @@ public final class PbsLexer {
|
|||||||
map.put("service", PbsTokenKind.SERVICE);
|
map.put("service", PbsTokenKind.SERVICE);
|
||||||
map.put("fn", PbsTokenKind.FN);
|
map.put("fn", PbsTokenKind.FN);
|
||||||
map.put("let", PbsTokenKind.LET);
|
map.put("let", PbsTokenKind.LET);
|
||||||
map.put("mut", PbsTokenKind.MUT);
|
|
||||||
map.put("declare", PbsTokenKind.DECLARE);
|
map.put("declare", PbsTokenKind.DECLARE);
|
||||||
map.put("struct", PbsTokenKind.STRUCT);
|
map.put("struct", PbsTokenKind.STRUCT);
|
||||||
map.put("contract", PbsTokenKind.CONTRACT);
|
map.put("contract", PbsTokenKind.CONTRACT);
|
||||||
|
|||||||
@ -1,37 +1,70 @@
|
|||||||
package p.studio.compiler.pbs.lexer;
|
package p.studio.compiler.pbs.lexer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token kinds produced by the PBS lexer.
|
||||||
|
*/
|
||||||
public enum PbsTokenKind {
|
public enum PbsTokenKind {
|
||||||
|
// End of file marker.
|
||||||
EOF,
|
EOF,
|
||||||
|
|
||||||
|
// User-defined names.
|
||||||
|
// Example: `sum`, `Vector`, `input_state`
|
||||||
IDENTIFIER,
|
IDENTIFIER,
|
||||||
|
|
||||||
|
// Literal values.
|
||||||
|
// Example: `42`, `3.14`, `255b`, `"hello"`
|
||||||
INT_LITERAL,
|
INT_LITERAL,
|
||||||
FLOAT_LITERAL,
|
FLOAT_LITERAL,
|
||||||
BOUNDED_LITERAL,
|
BOUNDED_LITERAL,
|
||||||
STRING_LITERAL,
|
STRING_LITERAL,
|
||||||
|
|
||||||
// Keywords
|
// Declaration and import keywords.
|
||||||
|
// Example:
|
||||||
|
// `import { Vec2 as V2 } from @core:math;`
|
||||||
IMPORT,
|
IMPORT,
|
||||||
FROM,
|
FROM,
|
||||||
AS,
|
AS,
|
||||||
|
|
||||||
|
// Barrel visibility keywords (reserved in source parser for now).
|
||||||
|
// Example in barrel:
|
||||||
|
// `pub fn sum;`
|
||||||
PUB,
|
PUB,
|
||||||
MOD,
|
MOD,
|
||||||
|
|
||||||
|
// Top-level declaration keywords.
|
||||||
|
// Example:
|
||||||
|
// `service Audio { fn play(): int { return 1; } }`
|
||||||
|
// `declare struct Point(x: int, y: int);`
|
||||||
SERVICE,
|
SERVICE,
|
||||||
FN,
|
FN,
|
||||||
LET,
|
|
||||||
MUT,
|
|
||||||
DECLARE,
|
DECLARE,
|
||||||
|
|
||||||
|
// Statement and type declaration keywords.
|
||||||
|
// Example:
|
||||||
|
// `let x: int = 1;`
|
||||||
|
LET,
|
||||||
STRUCT,
|
STRUCT,
|
||||||
CONTRACT,
|
CONTRACT,
|
||||||
ERROR,
|
ERROR,
|
||||||
|
|
||||||
|
// Control-flow keywords.
|
||||||
|
// Example:
|
||||||
|
// `if cond { return 1; } else { return 0; }`
|
||||||
IF,
|
IF,
|
||||||
ELSE,
|
ELSE,
|
||||||
WHEN,
|
WHEN,
|
||||||
FOR,
|
FOR,
|
||||||
IN,
|
IN,
|
||||||
RETURN,
|
RETURN,
|
||||||
|
|
||||||
|
// Boolean literals.
|
||||||
|
// Example: `true && false`
|
||||||
TRUE,
|
TRUE,
|
||||||
FALSE,
|
FALSE,
|
||||||
|
|
||||||
// Punctuation / operators
|
// Delimiters and separators.
|
||||||
|
// Example:
|
||||||
|
// `fn f(a: int) { return a; }`
|
||||||
LEFT_PAREN,
|
LEFT_PAREN,
|
||||||
RIGHT_PAREN,
|
RIGHT_PAREN,
|
||||||
LEFT_BRACE,
|
LEFT_BRACE,
|
||||||
@ -44,12 +77,18 @@ public enum PbsTokenKind {
|
|||||||
AT,
|
AT,
|
||||||
DOT_DOT,
|
DOT_DOT,
|
||||||
|
|
||||||
|
// Arithmetic and unary operators.
|
||||||
|
// Example: `-a + b * 2`
|
||||||
PLUS,
|
PLUS,
|
||||||
MINUS,
|
MINUS,
|
||||||
STAR,
|
STAR,
|
||||||
SLASH,
|
SLASH,
|
||||||
PERCENT,
|
PERCENT,
|
||||||
BANG,
|
BANG,
|
||||||
|
|
||||||
|
// Comparison and assignment operators.
|
||||||
|
// Example:
|
||||||
|
// `a == b`, `x <= y`, `value = 10`
|
||||||
BANG_EQUAL,
|
BANG_EQUAL,
|
||||||
EQUAL,
|
EQUAL,
|
||||||
EQUAL_EQUAL,
|
EQUAL_EQUAL,
|
||||||
@ -57,6 +96,9 @@ public enum PbsTokenKind {
|
|||||||
LESS_EQUAL,
|
LESS_EQUAL,
|
||||||
GREATER,
|
GREATER,
|
||||||
GREATER_EQUAL,
|
GREATER_EQUAL,
|
||||||
|
|
||||||
|
// Logical operators.
|
||||||
|
// Example: `a && b || c`
|
||||||
AND_AND,
|
AND_AND,
|
||||||
OR_OR
|
OR_OR
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,13 +115,11 @@ public final class PbsParser {
|
|||||||
if (!check(PbsTokenKind.RIGHT_PAREN)) {
|
if (!check(PbsTokenKind.RIGHT_PAREN)) {
|
||||||
do {
|
do {
|
||||||
final var pStart = peek();
|
final var pStart = peek();
|
||||||
final boolean mutable = match(PbsTokenKind.MUT);
|
|
||||||
final var pName = consume(PbsTokenKind.IDENTIFIER, "E_PARSE_EXPECTED_TOKEN", "Expected parameter name");
|
final var pName = consume(PbsTokenKind.IDENTIFIER, "E_PARSE_EXPECTED_TOKEN", "Expected parameter name");
|
||||||
consume(PbsTokenKind.COLON, "E_PARSE_EXPECTED_TOKEN", "Expected ':' after parameter name");
|
consume(PbsTokenKind.COLON, "E_PARSE_EXPECTED_TOKEN", "Expected ':' after parameter name");
|
||||||
final var typeRef = parseTypeRef();
|
final var typeRef = parseTypeRef();
|
||||||
parameters.add(new PbsAst.Parameter(
|
parameters.add(new PbsAst.Parameter(
|
||||||
pName.lexeme(),
|
pName.lexeme(),
|
||||||
mutable,
|
|
||||||
typeRef,
|
typeRef,
|
||||||
span(pStart.start(), typeRef.span().getEnd())));
|
span(pStart.start(), typeRef.span().getEnd())));
|
||||||
} while (match(PbsTokenKind.COMMA));
|
} while (match(PbsTokenKind.COMMA));
|
||||||
@ -174,7 +172,6 @@ public final class PbsParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private PbsAst.Statement parseLetStatement(final PbsToken letToken) {
|
private PbsAst.Statement parseLetStatement(final PbsToken letToken) {
|
||||||
final boolean mutable = match(PbsTokenKind.MUT);
|
|
||||||
final var name = consume(PbsTokenKind.IDENTIFIER, "E_PARSE_EXPECTED_TOKEN", "Expected variable name");
|
final var name = consume(PbsTokenKind.IDENTIFIER, "E_PARSE_EXPECTED_TOKEN", "Expected variable name");
|
||||||
|
|
||||||
PbsAst.TypeRef explicitType = null;
|
PbsAst.TypeRef explicitType = null;
|
||||||
@ -188,7 +185,6 @@ public final class PbsParser {
|
|||||||
|
|
||||||
return new PbsAst.LetStatement(
|
return new PbsAst.LetStatement(
|
||||||
name.lexeme(),
|
name.lexeme(),
|
||||||
mutable,
|
|
||||||
explicitType,
|
explicitType,
|
||||||
initializer,
|
initializer,
|
||||||
span(letToken.start(), semicolon.end()));
|
span(letToken.start(), semicolon.end()));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user