Better formatting and expression handling

This commit is contained in:
Martino Ferrari
2026-02-02 17:22:39 +01:00
parent 12615aa6d2
commit 749eab0a32
14 changed files with 735 additions and 67 deletions

View File

@@ -131,6 +131,7 @@ type VariableDefinition struct {
Name string
TypeExpr string
DefaultValue Value
IsConst bool
}
func (v *VariableDefinition) Pos() Position { return v.Position }

View File

@@ -20,6 +20,7 @@ const (
TokenBool
TokenPackage
TokenPragma
TokenLet
TokenComment
TokenDocstring
TokenComma
@@ -236,7 +237,21 @@ func (l *Lexer) lexString() Token {
}
func (l *Lexer) lexNumber() Token {
// Consume initial digits (already started)
// Check for hex or binary prefix if we started with '0'
if l.input[l.start:l.pos] == "0" {
switch l.peek() {
case 'x', 'X':
l.next()
l.lexHexDigits()
return l.emit(TokenNumber)
case 'b', 'B':
l.next()
l.lexBinaryDigits()
return l.emit(TokenNumber)
}
}
// Consume remaining digits
l.lexDigits()
if l.peek() == '.' {
@@ -255,6 +270,28 @@ func (l *Lexer) lexNumber() Token {
return l.emit(TokenNumber)
}
func (l *Lexer) lexHexDigits() {
for {
r := l.peek()
if unicode.IsDigit(r) || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') {
l.next()
} else {
break
}
}
}
func (l *Lexer) lexBinaryDigits() {
for {
r := l.peek()
if r == '0' || r == '1' {
l.next()
} else {
break
}
}
}
func (l *Lexer) lexDigits() {
for unicode.IsDigit(l.peek()) {
l.next()
@@ -321,6 +358,9 @@ func (l *Lexer) lexHashIdentifier() Token {
if val == "#package" {
return l.lexUntilNewline(TokenPackage)
}
if val == "#let" {
return l.emit(TokenLet)
}
return l.emit(TokenIdentifier)
}

View File

@@ -99,6 +99,8 @@ func (p *Parser) Parse() (*Configuration, error) {
func (p *Parser) parseDefinition() (Definition, bool) {
tok := p.next()
switch tok.Type {
case TokenLet:
return p.parseLet(tok)
case TokenIdentifier:
name := tok.Value
if name == "#var" {
@@ -286,7 +288,11 @@ func (p *Parser) parseAtom() (Value, bool) {
}, true
case TokenNumber:
if strings.Contains(tok.Value, ".") || strings.Contains(tok.Value, "e") {
isFloat := (strings.Contains(tok.Value, ".") || strings.Contains(tok.Value, "e") || strings.Contains(tok.Value, "E")) &&
!strings.HasPrefix(tok.Value, "0x") && !strings.HasPrefix(tok.Value, "0X") &&
!strings.HasPrefix(tok.Value, "0b") && !strings.HasPrefix(tok.Value, "0B")
if isFloat {
f, _ := strconv.ParseFloat(tok.Value, 64)
return &FloatValue{Position: tok.Position, Value: f, Raw: tok.Value}, true
}
@@ -409,6 +415,58 @@ func (p *Parser) parseVariableDefinition(startTok Token) (Definition, bool) {
}, true
}
func (p *Parser) parseLet(startTok Token) (Definition, bool) {
nameTok := p.next()
if nameTok.Type != TokenIdentifier {
p.addError(nameTok.Position, "expected constant name")
return nil, false
}
if p.next().Type != TokenColon {
p.addError(nameTok.Position, "expected :")
return nil, false
}
var typeTokens []Token
startLine := nameTok.Position.Line
for {
t := p.peek()
if t.Position.Line > startLine || t.Type == TokenEOF {
break
}
if t.Type == TokenEqual {
break
}
typeTokens = append(typeTokens, p.next())
}
typeExpr := ""
for _, t := range typeTokens {
typeExpr += t.Value + " "
}
var defVal Value
if p.next().Type != TokenEqual {
p.addError(nameTok.Position, "expected =")
return nil, false
}
val, ok := p.parseValue()
if ok {
defVal = val
} else {
return nil, false
}
return &VariableDefinition{
Position: startTok.Position,
Name: nameTok.Value,
TypeExpr: strings.TrimSpace(typeExpr),
DefaultValue: defVal,
IsConst: true,
}, true
}
func (p *Parser) Errors() []error {
return p.errors
}